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

  #############################################################################
  #
  # Copyright (c) 2016 Dell Computer Corporation
  # by Srinivas Gowda <srinivas_g_gowda@dell.com>
  # Dual Licenced under GNU GPL and OSL
  #
  #############################################################################
"""smbios-battery-ctl"""



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

# the following vars are all substituted on install
# this bin isnt byte-compiled, so this is ok
pythondir="/usr/lib/python3.8/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.2"

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

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

class RunTimeBatteryErr(Exception): pass


def command_parse():
    parser = cli.OptionParser(usage=__doc__, version=__VERSION__)
    cli.addStdOptions(parser, passwordOpts=True, securityKeyOpt=True)
    parser.add_option("--battery-charge", "-c", action="store_true", default=False, help=_("This will get the Current Battery Charging State"))

    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 RunTimeBatteryErr( "Internal Error: Byte position out of bound - ", byte_pos )
    except RunTimeBatteryErr as e:
        print("\t%s" % e)

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


def print_express_charge_status(n):
    if n == 0: print('\t Battery is not present')
    elif n == 1: print('\t Standard')
    elif n == 2: print('\t Express')
    elif n == 3: print('\t One-time express')
    elif n == 0xFF: print('\t Battery is present, and it does not support express charge')
    else : print('\t Unknown')


def PrintBatteryChargingState():
    try:
        res = smi.simple_ci_smi( 4, 12, 0 )
        verboseLog.info( _(" Get Charging 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 RunTimeBatteryErr( _(" Info: Unable to read Current Battery Charging State on this system\n") )

        # cbRES2 byte 0 gives supported Features
        print(_(" \n Supported battery charging features: "))
        if is_set (res[smi.cbRES2], 0): print("\t Express Charging")
        if is_set (res[smi.cbRES2], 1): print("\t Charge Disable")
        if (get_byte(res[smi.cbRES2], 0) == 0 ): print("\t NIL")

        print(_(" \n Battery charging Status: "))
        # cbRES2 byte 1 gives the status of supported Features
        if is_set ((get_byte(res[smi.cbRES2], 1)), 0): print("\t One or more batteries are present that support Express charge")
        if is_set ((get_byte(res[smi.cbRES2], 1)), 1): print("\t Charging is disabled")
        if (get_byte(res[smi.cbRES2], 1) == 0 ): print("\t NIL")

        # cbRes3, bytes 0-3 contain the status of batteries 0-3 respectively
        for i in range(4):
            expr_status = get_byte(res[smi.cbRES3], i)
            if(expr_status):
                print(_("\n Battery %d Express Charge State:") % (i))
                print_express_charge_status(expr_status)

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


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

    try:
        if options.battery_charge:
            print(_("Libsmbios version : %s") % sysinfo.get_library_version_string())
            print(_("smbios-battery-ctl version : %s") % __VERSION__)
            verboseLog.info( _(" You can use smbios-battery-ctl utility to view/modify battery settings"))
            PrintBatteryChargingState()

    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, 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 (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 12
# Battery Charging
#
# cbArg1 determines the function to be performed
# On return:
# cbRES1         Standard return codes (0, -1, -2)
#
# cbArg1 0x0 = Get Current State
# cbRes2, byte 0	Supported feature bits
#	bit 0	Express charge is supported (by the BIOS)
#	bit 1	Charge disable is supported
#	bits 2-7	Reserved for future use
# cbRes2, byte 1	Status bits
#	bit 0	One or more batteries are present that support Express charge
#	bit 1	Charging is disabled
#	bits 2-7	Reserved for future use
# If the system supports Express Charge, cbRes3, bytes 0-3 contain the status of batteries 0-3 respectively (numbered as with the Class 8 function).
# cbRes3, byte n	Battery n express charge status:
#	0	Battery is not present
#	1	Standard
#	2	Express
#	3	One-time express
#	0xFF	Battery is present, and it does not support express charge
#
# cbArg2, byte 0	Set charge disable state:
#	0	Off (enable charging)
#	1	On (disable charging)
#
# cbArg2, bytes 0-3 contain the new state of batteries 0-3 respectively
# 	Battery Status:
#	00h	High
#	01h	Low
#	02h 	Critical
#	03h	Charging
#
# cbArg2, byte n Battery n state:
#	0	No change (state remains as it was previously)
#	1	Standard . Express charge not supported by the battery
#	2	Express
#	3	One-time express
#
#
# cbClass 8
# cbSelect 16
# Set Battery Desktop Mode
#
# cbArg1 determines the function to be performed
# On return:
# cbRES1         Standard return codes (0, -1, -2)
#
# cbArg1, byte 0
#	Mode Enablement
#	0		Disable desktop mode
#	1		Fully enable desktop mode
#	2 - 255	Undefined
#
# On return:
# cbRES1         Standard return codes (0, -1, -2)
#
#
# cbClass 8
# cbSelect 17
# Get Battery Desktop Mode
#
# On return:
# cbRES1         Standard return codes (0, -1, -2)
#
# cbRes2	Status
#		Bit 0		1 = Supported, 0 = Not Supported
#		Bit 1		1 = Desktop Mode, 0 = Laptop Mode
#		Bit 2		1 = Battery is ready to enter desktop mode, 0 = Not ready
#		Bit 3 to 31	Unused
