#!/usr/bin/python3
# vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:tw=0

  #############################################################################
  #
  # Copyright (c) 2014 Dell Computer Corporation
  # Copyright (c) 2014 Pali Rohar <pali.rohar@gmail.com>
  # Dual Licenced under GNU GPL and OSL
  #
  #############################################################################
"""smbios-keyboard-ctl"""



# import arranged alphabetically
import gettext
import locale
import os
import sys
import traceback
import re

# the following vars are all substituted on install
# this bin isnt byte-compiled, so this is ok
pythondir="/usr/lib/python3.13/site-packages"
clidir="/usr/share/smbios-utils"
# end vars

# import all local modules after this.
sys.path.insert(0,pythondir)
sys.path.insert(0,clidir)

import cli
from libsmbios_c import smbios_token, smbios, smi, system_info as sysinfo, localedir, GETTEXT_PACKAGE
from libsmbios_c.trace_decorator import traceLog, getLog

__VERSION__="2.4.3"

locale.setlocale(locale.LC_ALL, '')
gettext.install(GETTEXT_PACKAGE, localedir)

moduleLog = getLog()
verboseLog = getLog(prefix="verbose.")

class RunTimeKeyboardErr(Exception): pass

def command_parse():
    parser = cli.OptionParser(usage=__doc__, version=__VERSION__)

    parser.add_option("--info", "-i", action="store_true", default=False, help=_("This will Display the Supported Features for Keyboard illumination"))
    parser.add_option("--get-status", "-g",  action="store_true", default=False, help=_("This will Display the current Status of Keyboard Illumination Settings"))
    parser.add_option("--set-mode", action="store_true", help=_("Option to set USER Selectable Keyboard Mode"))
    parser.add_option("--set-trigger" , action="store_true", help=_("Option to set Trigger types for Keyboard Illumination  "))
    parser.add_option("--set-timeout-battery", action="store", dest="set_timeout_battery", help=_("Set Keyboard Illumination Timeout Value (battery)"))
    parser.add_option("--set-timeout-ac", action="store", dest="set_timeout_ac", help=_("Set Keyboard Illumination Timeout Value (ac)"))
    parser.add_option("--set-level", action="store", dest="set_level", help=_("Set Keyboard Illumination Level"))
    parser.add_option("--als", action="store", dest="als_option", help=_("Set/Get ALS (Ambient Light Sensor Status and Control Level)"))

    cli.addStdOptions(parser, passwordOpts=True, securityKeyOpt=True)

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit()

    return parser.parse_args()


def is_set(num, pos):
    mask = 1 << pos
    return True if (num & mask) else False


# Return a the byte specified by byte_pos from a double word num
def get_byte(num, byte_pos):
    try :
        if byte_pos < 0 or byte_pos > 3:
            raise RunTimeKeyboardErr( "Internal Error: Byte position out of bound - ", byte_pos )
    except RunTimeKeyboardErr as e:
        print("\t%s" % e)

    return ((num >> (byte_pos*8)) & 0xFF)


def get_mode_settings():
    try:
        res = smi.simple_ci_smi( 4, 11, 0 )
        verboseLog.info( _("Get Keyboard Illumination Capability: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X,res[smi.cbRES4]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3],res[smi.cbRES4]) )

        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _("\ttCould not retrieve supported USER Selectable Modes") )

        #remove the Always ON bit, it should not be supported on most systems
        res[smi.cbRES2] &= 0xFFFD

        print(_("\n Supported USER Selectable Modes: "))
        print(_("\tOption\tMode"))
        print(_("------------------------------------------------"))
        if is_set (res[smi.cbRES2], 0): print ("\t0\tAlways OFF")
        if is_set (res[smi.cbRES2], 1): print ("\t1\tAlways ON")
        if is_set (res[smi.cbRES2], 2): print ("\t2\tAuto: ALS-based ON")
        if is_set (res[smi.cbRES2], 3): print ("\t3\tAuto: ALS- and input-activity-based On; input-activity based Off")
        if is_set (res[smi.cbRES2], 4): print ("\t4\tAuto: Input-activity-based On; input-activity based Off")
        if is_set (res[smi.cbRES2], 5): print ("\t5\tAuto: Input-activity-based On (illumination level 25%); input-activity based Off")
        if is_set (res[smi.cbRES2], 6): print ("\t6\tAuto: Input-activity-based On (illumination level 50%); input-activity based Off")
        if is_set (res[smi.cbRES2], 7): print ("\t7\tAuto: Input-activity-based On (illumination level 75%); input-activity based Off")
        if is_set (res[smi.cbRES2], 8): print ("\t8\tAuto: Input-activity-based On (illumination level 100%); input-activity based Off")

        print(_("\n Note: some modes might need the keyboard set at specific illumination levels\n"))
        try:
            val = int(input("Select Mode: "))

        except ValueError:
            raise RunTimeKeyboardErr("Invalid Mode detected")

        if val >= 0 and val <= 8 and is_set (res[smi.cbRES2], val):
            return 1 << val

        raise RunTimeKeyboardErr("Invalid Mode detected")

    except RunTimeKeyboardErr as e:
        print("\t%s" % e)


def get_trigger_settings():
    """ This function will print the current trigger capabalities and return the trigger map [0,1,3] selected by the user"""
    try:
        # Get the supported trigger types.
        res = smi.simple_ci_smi( 4, 11, 0 )
        verboseLog.info( _("Get Keyboard Illumination Capability: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X,res[smi.cbRES4]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3],res[smi.cbRES4]) )

        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _("\tCould not retrieve supported KeyBoard Illumination Capabilities") )

        print(_("\n Select Keyboard illumination on: "))
        print(_("\tTrigger Type\t\tOption"))
        print(_("------------------------------------------------"))
        if is_set (res[smi.cbRES3], 0): print ("\tAny Keystroke     \t 0")
        if is_set (res[smi.cbRES3], 1): print ("\tTouchpad activity \t 1")
        if is_set (res[smi.cbRES3], 2): print ("\tPointing stick    \t 2")
        if is_set (res[smi.cbRES3], 3): print ("\tAny mouse movement\t 3")

        print(_("Eg: to Select Trigger for [Any Keystroke] and [Touchpad activity] specify your option like this 0,1)" ))
        val = input("Trigger on -")
        verboseLog.info ( _('Selected %s') % val)
        trigger_opt = val.split(',')
        trigger=[]

        try:
            if val != "":
                for opt in trigger_opt:
                    if int(opt) < 0 or int(opt) > 3 or not is_set (res[smi.cbRES3], int(opt)):
                        raise RunTimeKeyboardErr( "Invalid trigger options detected. Selected option not in range")
                    else: trigger.append(int(opt))

        except ValueError:
            raise RunTimeKeyboardErr( "Invalid Trigger options detected")

        print(_("Triggers Validated Successfully"), trigger)
        return trigger

    except RunTimeKeyboardErr as e:
        print("\t%s" % e)

#To-do
#  - ALS Setting
#  - Keyboard light level
def _set_keyboard_attr( reg, flag='default'):
    """A Generic function to sets keyboard illuminations settings.Flag="trigger", set keyboard illumination "Trigger" settings. Flag="timeout", set keyboard illumination "Timeout" settings."""

    if flag != 'trigger' and flag != 'timeout-ac' and flag != 'timeout-battery' and flag != 'mode' and flag != 'level':
        raise RunTimeKeyboardErr( _("\t _set_keyboard_attr Flag error: %s  ") % flag )

    # Get Current settings. Need this to write back flags that we are not interested in
    try:
        res = smi.simple_ci_smi( 4, 11, 1 )
        verboseLog.info( _("Get Keyboard illumination Current State: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]) )

        if res[smi.cbRES1] != 0 :
            raise RunTimeKeyboardErr( _("\t Failed to set Keyboard illumination attributes. Could not read the current settings") )

        mode_state = res[smi.cbRES2] & 0xFFFF
        trigger_map = get_byte(res[smi.cbRES2] ,2)
        timeout_bat = get_byte(res[smi.cbRES2] ,3)
        timeout_ac =  get_byte(res[smi.cbRES3] ,3)
        asl_setting = get_byte(res[smi.cbRES3] ,0)
        keybrd_light_level = get_byte(res[smi.cbRES3] ,2)

        if flag == 'trigger':
            trigger_map = 0
            if reg is None:
                raise RunTimeKeyboardErr( _("\t No valid trigger defined") )
            for opt in reg:
                trigger_map|=1<<opt

        elif flag == 'timeout-ac':
            if reg == 0:
                raise RunTimeKeyboardErr( _("\t A timeout of zero is not supported") )
            timeout_ac = reg

        elif flag == 'timeout-battery':
            if reg == 0:
                raise RunTimeKeyboardErr( _("\t A timeout of zero is not supported") )
            timeout_bat = reg

        elif flag == 'mode':
            mode_state = reg

        elif flag == 'level':
            keybrd_light_level = reg
            if keybrd_light_level != 0 and mode_state & 1<<0:
                print(_("\n The current mode (Always OFF) might prevent level changes, select a new mode"))
                mode_state = get_mode_settings()

        # Now lets write Arg2 and Arg3, 4 bytes long each
        # Just do a blind write of reserved region as well.
        cbArg2_w0 = mode_state
        cbArg2_b2 = trigger_map << 16
        cbArg2_b3 = timeout_bat << 24

        cbArg3_b0 = asl_setting
        cbArg3_b1 = 0x00
        cbArg3_b2 = keybrd_light_level << 16
        cbArg3_b3 = timeout_ac << 24

        Arg2 = cbArg2_b3 | cbArg2_b2 | cbArg2_w0
        Arg3 = cbArg3_b0 | cbArg3_b1 | cbArg3_b2 | cbArg3_b3

        verboseLog.info( _("Setting new params in _set_keyboard_attr : Arg2=0x%X Arg3=0x%X") % (Arg2, Arg3) )
        res = smi.simple_ci_smi( 4, 11, 2, Arg2, Arg3)
        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _(" Set Trigger Failed. Failed to write config") )

        print(( _("\nNew %s parameter set Successfully\n") % (flag)))

    except RunTimeKeyboardErr as e:
        print("\t %s" % e)
        print(( _("\t Error Return Code : cbRES1: 0x%X cbRES2: 0x%X cbRES3: 0x%X ") % (res[smi.cbRES1], res[smi.cbRES3], res[smi.cbRES4]) ))


def set_mode():
    mode=get_mode_settings()
    if (mode):
        _set_keyboard_attr(mode, flag='mode')

def set_trigger():
    trigger=get_trigger_settings()
    _set_keyboard_attr(trigger, flag='trigger')

    return 0

def print_current_timeout():

    try:
        res = smi.simple_ci_smi( 4, 11, 1 )
        verboseLog.info( _("Get Keyboard illumination Current State: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]) )

        if res[smi.cbRES1] != 0 :
            raise RunTimeKeyboardErr( _("\t Error reading keyboard settings."), res[smi.cbRES1], res[smi.cbRES2], res[smi.cbRES3] )

        #cbRes2 byte2
        trigger_map = get_byte(res[smi.cbRES2] ,2)
        print(_("\n Your Keyboard will illumination on: "))
        if is_set (trigger_map, 0): print ("\tAny Keystroke")
        if is_set (trigger_map, 1): print ("\tTouchpad activity")
        if is_set (trigger_map, 2): print ("\tPointing stick")
        if is_set (trigger_map, 3): print ("\tAny mouse movement")
        #Note - Touchpad activity & Pointing stick seems to be symbiotic the system that I used!!!

        #Timeout battery units indicator cbRes2 byte3 [7:6]
        t_unit = (get_byte(res[smi.cbRES2] ,3) & 0xC0) >> 6
        time_unit=[' Seconds', 'Minutes', 'Hours', 'Days'][t_unit]

        #Timeout battery value cbRes2 byte3 [5:0]
        timeout = get_byte(res[smi.cbRES2] ,3) & 0x3F

        print(_("\nOld Keyboard illumination battery timeout is: %d %s") % (timeout, time_unit))

        if supports_ac_timeout():
            #Timeout ac units indicator cbRes3 byte2 [7:6]
            t_unit = (get_byte(res[smi.cbRES3] ,3) & 0xC0) >> 6
            time_unit=[' Seconds', 'Minutes', 'Hours', 'Days'][t_unit]

            #Timeout ac value cbRes3 byte3 [5:0]
            timeout = get_byte(res[smi.cbRES3] ,3) & 0x3F
            print(_("\nOld Keyboard illumination AC timeout is: %d %s") % (timeout, time_unit))

    except RunTimeKeyboardErr as e:
        print("\t%s" % e)


def timeout_parser(s_timeout):
    try:

        #Bit of regEx magic to check and validate timeout value
        ret=re.findall(r'^\d\w{0,1}[smhd]', s_timeout)
        if len(ret) != 1: raise RunTimeKeyboardErr ( _("\t Invalid Timeout parameters detected"))

        t_units = ['s', 'm', 'h', 'd']
        try:
            t_unit = t_units.index(ret[0][-1])
            timeout = int(ret[0][:-1])
        except (IndexError, ValueError):
            print(_("\n Error: Invalid Timeout entry detected"))
            raise RunTimeKeyboardErr

        if timeout < 0 or timeout > 63:
            raise RunTimeKeyboardErr ( _("\t Timeout value out of range: %d") % timeout )

        print(("Setting keyboard illumination timeout value  of:  %d%s") % (timeout, t_units[t_unit]))
        return (timeout, t_unit)

    except RunTimeKeyboardErr as e:
        print("%s" % e)
        print(_("\n To set timeout value as 45 seconds enter: 45s "))
        print(_(" You can specify time out values in  s[Seconds], m[Minutes], h[hours], d[days]"))
        print(_(" Timeout value range is 1-63 "))
        return (-1,-1)


def set_KeyBoardIllumination_timeout(s_timeout, timeout_type):
    """ This function will set a new Keyboard Illumination timeout value"""

    (timeout, t_unit) = timeout_parser(s_timeout)
    if (timeout, t_unit) == (-1, -1): return -1

    # Unlike set_trigger, here I print the current keyboard illumination timeout value and not the capability.
    # Printing the current timeout settings seems more apt than printing the capabilitie
    print_current_timeout()
    _set_keyboard_attr((t_unit << 6 | timeout) , flag='timeout-%s' % timeout_type)

    return 0


def print_current_level():
    try:
        res = smi.simple_ci_smi( 4, 11, 1 )
        verboseLog.info( _("Get Keyboard illumination Current State: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]) )

        if res[smi.cbRES1] != 0 :
            raise RunTimeKeyboardErr( _("\t Error reading keyboard settings."), res[smi.cbRES1], res[smi.cbRES2], res[smi.cbRES3] )

        print(_("\nOld Keyboard illumination level is: %d") % (get_byte(res[smi.cbRES3], 2)))

    except RunTimeKeyboardErr as e:
        print("\t%s" % e)


def set_KeyBoardIllumination_level(s_level):
    """ This function will set a new Keyboard Illumination level value"""

    try:
        res = smi.simple_ci_smi( 4, 11, 0 )
        range = get_byte(res[smi.cbRES3], 2)
        level = int(s_level)
        if range == 0:
            raise RunTimeKeyboardErr(_("\n Level setting not supported"))

        if level < 0 or level > range:
            raise RunTimeKeyboardErr(_("\n Value out of range, range is 0-%d") % range)

        print_current_level()
        _set_keyboard_attr(level, flag='level')

    except RunTimeKeyboardErr as e:
        print("\t%s" % e)


def PrintKeyBoardStatus():
    print(_("\n Current status of KeyBoard Illumination setting on your system: "))
    print(_("-------------------------------------------------------------------"))

    try:
        #Get Keyboard Illumination Configuration,
        res = smi.simple_ci_smi( 4, 9 )
        verboseLog.info( _("Get Keyboard Illumination Configuration: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]) )

        if res[smi.cbRES1] != 0:
            verboseLog.info( _(" Info: Unable to fetch Keyboard Illumination Configuration information: Feature Not supported on this system\n") )

        else :
            illu_mode = ['Automatic', 'Always OFF', 'Always ON']
            try:
                print(("Current Keyboard Illumination mode is: %s") % (illu_mode [res[smi.cbRES1] & 0xFF]))
            except IndexError:
                print(_("\t Error: Detected Invalid Keyboar Illumination mode"))
                raise RunTimeKeyboardErr

            if res[smi.cbRES2] < 0:
                raise RunTimeKeyboardErr( _("Failed to read: Ambient Light trigger level") )

            print(_("\t Ambient Light level to trigger automatic keyboard illumination: 0x%X") % ((res[smi.cbRES2]>>8) & 0xFF))
            print(_("\t Last known Ambient Light level to trigger automatic keyboard illumination: 0x%X") % ((res[smi.cbRES2]>>16) & 0xFF))

        res = smi.simple_ci_smi( 4, 11, 1 )
        verboseLog.info( _("Get Keyboard illumination Current State: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]) )

        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _("\tYour Keyboard may not support Keyboard illumination Feature") )

        print(_("\n Configured mode state: "))
        if   is_set (res[smi.cbRES2], 0): print("\t Always OFF")
        elif is_set (res[smi.cbRES2], 1): print("\t Always ON")
        elif is_set (res[smi.cbRES2], 2): print("\t Auto: ALS-based ON")
        elif is_set (res[smi.cbRES2], 3): print("\t Auto: ALS- and input-activity-based On; input-activity based Off")
        elif is_set (res[smi.cbRES2], 4): print("\t Auto: Input-activity-based On; input-activity based Off")
        elif is_set (res[smi.cbRES2], 5): print("\t Auto: Input-activity-based On (illumination level 25%); input-activity based Off")
        elif is_set (res[smi.cbRES2], 6): print("\t Auto: Input-activity-based On (illumination level 50%); input-activity based Off")
        elif is_set (res[smi.cbRES2], 7): print("\t Auto: Input-activity-based On (illumination level 75%); input-activity based Off")
        elif is_set (res[smi.cbRES2], 8): print("\t Auto: Input-activity-based On (illumination level 100%); input-activity based Off")
        else : print("\t Reserved setting")
        #[9:15] resev

        #cbRes2 byte2
        trigger_map = get_byte(res[smi.cbRES2] ,2)
        print(_("\n Your Keyboard will illumination on: "))
        if is_set (trigger_map, 0): print ("\tAny Keystroke")
        if is_set (trigger_map, 1): print ("\tTouchpad activity")
        if is_set (trigger_map, 2): print ("\tPointing stick")
        if is_set (trigger_map, 3): print ("\tAny mouse movement")
        #Note - Touchpad activity & Pointing stick seems to be symbiotic

        #Timeout units indicator cbRes2 byte2 [7:6]
        t_unit = (get_byte(res[smi.cbRES2] ,3) & 0xC0) >> 6
        time_unit=[' Seconds', 'Minutes', 'Hours', 'Days'][t_unit]

        #Timeout value cbRes2 byte2 [5:0]
        timeout = get_byte(res[smi.cbRES2] ,3) & 0x3F

        print(_("\n Keyboard illumination timeout on battery has been set at: %d %s") % (timeout, time_unit))

        if supports_ac_timeout():
            #Timeout units indicator cbRes3 byte3 [7:6]
            t_unit = (get_byte(res[smi.cbRES2] ,3) & 0xC0) >> 6
            time_unit=[' Seconds', 'Minutes', 'Hours', 'Days'][t_unit]

            #Timeout value cbRes3 byte3 [5:0]
            timeout = get_byte(res[smi.cbRES3] ,3) & 0x3F
            print(_("\n Keyboard illumination timeout on AC has been set at: %d %s") % (timeout, time_unit))

        #cbRes3 byte0
        print(_("\n Current setting of ALS value that turns the light on or off: %d") % ( get_byte(res[smi.cbRES3] ,0)))
        #cbRes3 byte1
        print(_(" Current ALS Reading : %d") % ( get_byte(res[smi.cbRES3] ,1)))
        #cbRes3 byte2
        print(_(" Current keyboard light level : %d") % ( get_byte(res[smi.cbRES3] ,2)))

    except RunTimeKeyboardErr as e:
        print("\t%s" % e)


def PrintKeyBoardCapab():

    """ This function prints the Supported KeyBoard Illumination features"""

    print(_("\n Capabilities of KeyBoard Illumination on your system: "))
    print(_("-------------------------------------------------------------------"))

    try:
        res = smi.simple_ci_smi( 4, 11, 0 )
        verboseLog.info( _("Get Keyboard Illumination Capability: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X,res[smi.cbRES4]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3],res[smi.cbRES4]) )

        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _("Warning:: Your Keyboard may not support Keyboard illumination Feature") )

        #remove the Always ON bit, it should not be supported on most systems
        res[smi.cbRES2] &= 0xFFFD

        print(_(" Supported USER Selectable Modes : "))
        if is_set (res[smi.cbRES2], 0): print("\t Always OFF")
        if is_set (res[smi.cbRES2], 1): print("\t Always ON")
        if is_set (res[smi.cbRES2], 2): print("\t Auto: ALS-based ON")
        if is_set (res[smi.cbRES2], 3): print("\t Auto: ALS- and input-activity-based On; input-activity based Off")
        if is_set (res[smi.cbRES2], 4): print("\t Auto: Input-activity-based On; input-activity based Off")
        if is_set (res[smi.cbRES2], 5): print("\t Auto: Input-activity-based On (illumination level 25%); input-activity based Off")
        if is_set (res[smi.cbRES2], 6): print("\t Auto: Input-activity-based On (illumination level 50%); input-activity based Off")
        if is_set (res[smi.cbRES2], 7): print("\t Auto: Input-activity-based On (illumination level 75%); input-activity based Off")
        if is_set (res[smi.cbRES2], 8): print("\t Auto: Input-activity-based On (illumination level 100%); input-activity based Off")

        #byte 3 byt 0,1,2 mask
        keyboard_type=['Reserved', 'Tasklight', 'Backlight', 'Reserved for Future']
        key = get_byte(res[smi.cbRES2] ,3)
        print(_("\n Supported Keyboard illumination type : "), keyboard_type[key] if key < 3 else keyboard_type[3])

        #cbRes3 byte0
        print(_("\n Supports Keyboard illumination on : "))
        if is_set (res[smi.cbRES3], 0): print ("\tAny Keystroke")
        if is_set (res[smi.cbRES3], 1): print ("\tTouchpad activity")
        if is_set (res[smi.cbRES3], 2): print ("\tPointing stick")
        if is_set (res[smi.cbRES3], 3): print ("\tAny mouse movement")

        #cbRes3 byte1
        print(_("\n Can configure Keyboard illumination timeout unit in : "))
        if is_set (res[smi.cbRES3], 8) : print ("\tSeconds")
        if is_set (res[smi.cbRES3], 9) : print ("\tMinutes")
        if is_set (res[smi.cbRES3], 10): print ("\tHours")
        if is_set (res[smi.cbRES3], 11): print ("\tDays")

        #cbRes3 byte2
        print(_("\n Supported Keyboard light brightness levels : "), get_byte(res[smi.cbRES3], 2))

        #cbRes4 byte0
        print(_("\n Maximum acceptable seconds timeout value   : "), get_byte(res[smi.cbRES4], 0))

        #cbRes4 byte1
        print(_("\n Maximum acceptable minutes timeout value   : "), get_byte(res[smi.cbRES4], 1))

        #cbRes4 byte2
        print(_("\n Maximum acceptable hours timeout value     : "), get_byte(res[smi.cbRES4], 2))

        #cbRes4 byte3
        print(_("\n Maximum acceptable days timeout value      : "), get_byte(res[smi.cbRES4], 3))

    except RunTimeKeyboardErr as e:
        print("\t %s" % e)
        print(( _("\t Error Return Code: cbRES1: 0x%X cbRES2: 0x%X cbRES3: 0x%X cbRES4: 0x%X ") % (res[smi.cbRES1], res[smi.cbRES2], res[smi.cbRES3], res[smi.cbRES4]) ))


def get_als_status():
    """This function is used to get the Ambient Light Sensor Status(ALS)"""

    try:
        res = smi.simple_ci_smi( 17, 10, 0 )
        verboseLog.info( _("Get Ambient Light Sensor(ALS) Status: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]))

        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _("Unable to get ALS settings: Feature Not supported on this system\n"))

        print("Current ALS Status: ")
        #cbArg2,byte0 - 0/1, this should return 0 or 1.
        print('Enabled' if (get_byte(res[smi.cbRES2] ,0) ) else 'Disabled')

        print("Current ALS low limit  : ", get_byte(res[smi.cbRES3] ,0))
        print("Current ALS High limit : ", get_byte(res[smi.cbRES3] ,1))
        print("Absolute ALS low limit : ", get_byte(res[smi.cbRES3] ,2))
        print("Absolute ALS High limit: ", get_byte(res[smi.cbRES3] ,3))

    except RunTimeKeyboardErr as e:
        print("\t %s" % e)
        print(( _("\t Error Return Code : cbRES1: 0x%X cbRES2: 0x%X cbRES3: 0x%X ") % (res[smi.cbRES1], res[smi.cbRES3], res[smi.cbRES4]) ))


def print_als_brightness_table():
    """This function is used to fetch the ALS Brightness Table"""

    try:
        res = smi.simple_ci_smi( 17, 10, 2 )
        verboseLog.info( _("Get Ambient Light Sensor(ALS) Brightness Table: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]))

        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _("Unable to get ALS Brightness Table: Feature Not supported on this system\n"))

        print("Brightness Table: ", hex(res[smi.cbRES3])+hex(res[smi.cbRES2])[2:])

    except RunTimeKeyboardErr as e:
        print("\t %s" % e)
        print(( _("\t Error Return Code : cbRES1: 0x%X cbRES2: 0x%X cbRES3: 0x%X ") % (res[smi.cbRES1], res[smi.cbRES3], res[smi.cbRES4]) ))


def validate_als_limit(s_val):
    int_als = 0
    try:
        int_als = int(s_val)
        verboseLog.info( _("ALS high/low limit: %d ") % (int_als))
        return int_als

    except (TypeError, ValueError):
        print("Enter the ALS value as integer: ", s_val)
        raise RunTimeKeyboardErr("Invalid Integer valie detected")


def set_als_param(parm_str='NA', parm=0):
    """This function is used to Enable/disable ALS and also the ALS high and low limits"""

    try:
        res = smi.simple_ci_smi( 17, 10, 0 )
        verboseLog.info( _("Get Ambient Light Sensor(ALS) Status: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]))

        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _("Unable to get ALS settings: Feature Not supported on this system\n"))

        # Get current settings
        status     = get_byte(res[smi.cbRES2] ,0 )
        low_limit  = get_byte(res[smi.cbRES3] ,0 )
        high_limit = get_byte(res[smi.cbRES3] ,1 )

        if parm_str == 'status':
            status = parm

        elif parm_str == 'als_high':
            high_limit = parm

        elif parm_str == 'als_low':
            low_limit = parm

        else:
            raise RunTimeKeyboardErr( _("Invalid parm_str detected \n"))

        cbArg2 =  high_limit << 16 | low_limit << 8 | status

        res = smi.simple_ci_smi( 17, 10, 1, cbArg2 )
        verboseLog.info( _("Set Ambient Light Sensor(ALS) Status: res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X,res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]))

        if res[smi.cbRES1] != 0:
            raise RunTimeKeyboardErr( _("Unable to Set ALS settings\n"))

        print("ALS parameter set successfully")

    except RunTimeKeyboardErr as e:
        print("\t %s" % e)
        print(( _("\t Error Return Code : cbRES1: 0x%X cbRES2: 0x%X cbRES3: 0x%X ") % (res[smi.cbRES1], res[smi.cbRES3], res[smi.cbRES4]) ))


def set_als_settings(als_param):

    if als_param == 'get':
        get_als_status()
        print_als_brightness_table()

    elif als_param == 'enable':
        set_als_param('status', 1)

    elif als_param == 'disable':
        set_als_param('status', 0)

    elif als_param[:8] == 'set-low=':
        als_low = validate_als_limit(als_param[9:])
        set_als_param('als_low', als_low)

    elif als_param[:9] == 'set-high=':
        als_high = validate_als_limit(als_param[9:])
        set_als_param('als_high', als_high)

    else:
        print("invalide --als [%s]" % als_param)
        print("\tget  		- Get ALS Settings  ")
        print("\tenable    	- Enable ALS  ")
        print("\tdisable   	- Disable ALS ")
        print("\tset-high=<int>  - set ALS High Limit ")
        print("\tset-low =<int>  - set ALS Low  Limit ")

def supports_ac_timeout():
    try:
        tokenTable = smbios_token.TokenTable()
        supports_ac = tokenTable[0x451]
        return True
    except IndexError:
        return False

def main():
    exit_code = 0
    (options, args) = command_parse()
    cli.setup_std_options(options)

    try:
        if options.info:
            print(_("Libsmbios version : %s") % sysinfo.get_library_version_string())
            print(_("smbios-keyboard-ctl version : %s") % __VERSION__)
            #By default just show the supported features of keyboard illumination
            PrintKeyBoardCapab()

        if options.get_status:
            print(_("Helper function to print current status of keyboard illumination"))
            PrintKeyBoardStatus()

        if options.set_mode:
            print(_("Setting USER Selectable Keyboard Mode"))
            set_mode()

        if options.set_trigger:
            print(_("Setting Keyboard illumination Triggers "))
            set_trigger()

        if options.set_timeout_battery:
            set_KeyBoardIllumination_timeout(options.set_timeout_battery, "battery")

        if options.set_timeout_ac:
            if supports_ac_timeout():
                set_KeyBoardIllumination_timeout(options.set_timeout_ac, "ac")
            else:
                raise RunTimeKeyboardErr("This system doesn't support AC timeout" )

        if options.set_level:
            set_KeyBoardIllumination_level(options.set_level)

        if options.als_option:
            set_als_settings(options.als_option)


    except (smi.SMIExecutionError, ) as e:
        exit_code=3
        moduleLog.info( _("ERROR: Could not execute SMI.") )
        verboseLog.info( _("The smi library returned this error:") )
        verboseLog.info( str(e) )
        moduleLog.info( cli.standardFailMessage )

    except (smbios.TableParseError, smbios_token.TokenTableParseError) as e:
        exit_code=3
        moduleLog.info( _("ERROR: Could not parse system SMBIOS table.") )
        verboseLog.info( _("The smbios library returned this error:") )
        verboseLog.info( str(e) )
        moduleLog.info( cli.standardFailMessage )

    except (smbios_token.TokenManipulationFailure,) as e:
        exit_code=4
        moduleLog.info( _("ERROR: Could not manipulate system token.") )
        verboseLog.info( _("The token library returned this error:") )
        verboseLog.info( str(e) )
        moduleLog.info( cli.standardFailMessage )

    return exit_code

if __name__ == "__main__":
    sys.exit( main() )


# cbClass 4
# cbSelect 11
# Keyboar illumination
# cbArg1 determines the function to be performed

# cbArg1 0x0 = Get Feature Information
#  cbRES1         Standard return codes (0, -1, -2)
#  cbRES2, word0  Bitmap of user-selectable modes
#     bit 0     Always off (All systems)
#     bit 1     Always on (Travis ATG, Siberia)
#     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
#     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
#     bit 4     Auto: Input-activity-based On; input-activity based Off
#     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
#     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
#     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
#     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
#     bits 9-15 Reserved for future use
#  cbRES2, byte2  Reserved for future use
#  cbRES2, byte3  Keyboard illumination type
#     0         Reserved
#     1         Tasklight
#     2         Backlight
#     3-255     Reserved for future use
#  cbRES3, byte0  Supported auto keyboard illumination trigger bitmap.
#     bit 0     Any keystroke
#     bit 1     Touchpad activity
#     bit 2     Pointing stick
#     bit 3     Any mouse
#     bits 4-7  Reserved for future use
#  cbRES3, byte1  Supported timeout unit bitmap
#     bit 0     Seconds
#     bit 1     Minutes
#     bit 2     Hours
#     bit 3     Days
#     bits 4-7  Reserved for future use
#  cbRES3, byte2  Number of keyboard light brightness levels
#  cbRES4, byte0  Maximum acceptable seconds value (0 if seconds not supported).
#  cbRES4, byte1  Maximum acceptable minutes value (0 if minutes not supported).
#  cbRES4, byte2  Maximum acceptable hours value (0 if hours not supported).
#  cbRES4, byte3  Maximum acceptable days value (0 if days not supported)

# cbArg1 0x1 = Get Current State
#  cbRES1         Standard return codes (0, -1, -2)
#  cbRES2, word0  Bitmap of current mode state
#     bit 0     Always off (All systems)
#     bit 1     Always on (Travis ATG, Siberia)
#     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
#     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
#     bit 4     Auto: Input-activity-based On; input-activity based Off
#     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
#     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
#     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
#     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
#     bits 9-15 Reserved for future use
#     Note: Only One bit can be set
#  cbRES2, byte2  Currently active auto keyboard illumination triggers.
#     bit 0     Any keystroke
#     bit 1     Touchpad activity
#     bit 2     Pointing stick
#     bit 3     Any mouse
#     bits 4-7  Reserved for future use
#  cbRES2, byte3  Current Timeout
#     bits 7:6  Timeout units indicator:
#     00b       Seconds
#     01b       Minutes
#     10b       Hours
#     11b       Days
#     bits 5:0  Timeout value (0-63) in sec/min/hr/day
#     NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte
#     are set upon return from the [Get feature information] call.
#  cbRES3, byte0  Current setting of ALS value that turns the light on or off.
#  cbRES3, byte1  Current ALS reading
#  cbRES3, byte2  Current keyboard light level.
#  cbRES3, byte3  Current timeout, on AC Power Bits
#     7:6 Timeout units indicator:
#     00b Seconds
#     01b Minutes
#     10b Hours
#     11b Days
#     Bits 5:0 Timeout value (0-63) in sec/min/hr/day
#     NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte2
#     are set upon return from the upon return from the [Get Feature information] call.
# cbArg1 0x2 = Set New State
#  cbRES1         Standard return codes (0, -1, -2)
#  cbArg2, word0  Bitmap of current mode state
#     bit 0     Always off (All systems)
#     bit 1     Always on (Travis ATG, Siberia)
#     bit 2     Auto: ALS-based On; ALS-based Off (Travis ATG)
#     bit 3     Auto: ALS- and input-activity-based On; input-activity based Off
#     bit 4     Auto: Input-activity-based On; input-activity based Off
#     bit 5     Auto: Input-activity-based On (illumination level 25%); input-activity based Off
#     bit 6     Auto: Input-activity-based On (illumination level 50%); input-activity based Off
#     bit 7     Auto: Input-activity-based On (illumination level 75%); input-activity based Off
#     bit 8     Auto: Input-activity-based On (illumination level 100%); input-activity based Off
#     bits 9-15 Reserved for future use
#     Note: Only One bit can be set
#  cbArg2, byte2  Desired auto keyboard illumination triggers. Must remain inactive to allow
#                 keyboard to turn off automatically.
#     bit 0     Any keystroke
#     bit 1     Touchpad activity
#     bit 2     Pointing stick
#     bit 3     Any mouse
#     bits 4-7  Reserved for future use
#  cbArg2, byte3  Desired Timeout
#     bits 7:6  Timeout units indicator:
#     00b       Seconds
#     01b       Minutes
#     10b       Hours
#     11b       Days
#     bits 5:0  Timeout value (0-63) in sec/min/hr/day
#  cbArg3, byte0  Desired setting of ALS value that turns the light on or off.
#  cbArg3, byte2  Desired keyboard light level.
#  cbArg3, byte3  Desired Timeout on AC power
#     bits 7:6  Timeout units indicator:
#     00b       Seconds
#     01b       Minutes
#     10b       Hours
#     11b       Days
#     bits 5:0  Timeout value (0-63) in sec/min/hr/day

# cbClass 17
# cbSelect 10
# Ambient Light Sensor Status and Control
# cbArg1 determines the function to be performed
#
# cbArg1 0x0 = Get ALS Status
# Set New Status
# cbRes2, byte 0 Current ALS Status
#  0 Disabled
#  1 Enabled
# cbRes3, byte 0 Current ALS low limit
# cbRes3, byte 1 Current ALS high limit
# cbRes3, byte 2 Absolute ALS low limit
# cbRes3, byte 3 Absolute ALS high limit
#
# cbArg1 0x1 = Control ALS
# cbArg2, byte 0
#  0 Disable ALS
#  1 Enable ALS
# cbArg2, byte 1 ALS low limit to set
# cbArg2, byte 2 ALS high limit to set
# On return:
# cbRes1 Standard return values (0, -1, -2)#
#
# cbArg1 0x2 = Get Brightness Table
# cbRes2 Bytes 3..0 of the brightness table
# cbRes3 Bytes 7..4 of the brightness table

# cbClass 4
# cbSelect 9
# Get Keyboard Illumination Configuration
# On return:
# cbRes1 Standard return values (0, -1, -2)
# cbRes2, byte 0 Current keyboard illumination mode:
#     0x00 - Automatic
#     0x01 - Always off
#     0x02 - Always on
# cbRes2, byte 1 Current automatic mode threshold
#     0x00-0xFF - Ambient light level to trigger automatic keyboard illumination
# cbRes2, byte 2 Current ambient light level
#     0x00-0xFF - Last know n light level read from the system ambient light sensor.
