#!/usr/bin/env python
# mopicli .This tool is for interfacing with the MoPi battery power add-on
#   board for the Raspberry Pi. (http://pi.gate.ac.uk/mopi)

import sys
import mopiapi
import errno

# Version of mopicli
CLIVERSION=0.5

# Version of API used
APIVERSION=0.3

# Package version
VERSION="4.1"

# Different categories for the mopicli arguments
categories = ["Miscellaneous", "Status", "Read configuration options", "Write configuration options"]

# flag (0), description (1), label (2), number of arguments (3), category (4)
# dash for label means to use the description
commands2 = [
	[  "-i", "I2C bus", "-", 1, 0],
	[ "-fv", "Firmware version", "-", 0, 0],
	[ "-sn", "Serial number", "-", 0, 0],
	[  "-h", "Display this message", "-", 0, 0],
	[  "-e", "Read everything possible from the MoPi", "-", 0, 0],
	[ "-nl", "No labels on output", "-", 0, 0],

	[  "-s", "MoPi status word", "Status word", 0, 1],
	[ "-sv", "Verbose status", "-", 0, 1],
	[  "-v", "Current source voltage in mV", "Current source voltage", 0, 1],
	[ "-v1", "Source #1 voltage in mV", "Source #1 voltage", 0, 1],
	[ "-v2", "Source #2 voltage in mV", "Source #2 voltage", 0, 1],

	[ "-rc", "Combined source #1/#2 conf", "Combined conf", 0, 2],
	["-rc1", "Source #1 conf", "-", 0, 2],
	["-rc2", "Source #2 conf", "-", 0, 2],
	["-ron", "Power on delay", "-", 0, 2],
	["-rsd", "Shutdown delay", "-", 0, 2],

	[ "-wc", "Combined source #1/#2 conf", "", 5, 3],
	["-wc1", "Source #1 conf", "", 5, 3],
	["-wc2", "Source #2 conf", "", 5, 3],
	["-won", "\tPower on timer in seconds", "", 1, 3],
	["-wsd", "\tShutdown timer in seconds", "", 1, 3],
	]

# command, parameters, details
parameters2 = [
	[  "-i", "x", "Where x is the I2C bus that connects the MoPi to the Raspberry Pi. If ths\n    flag is not set, the default bus of \x1b[1m%i\x1b[0m is used, based on board revision." % mopiapi.guessI2C()],
	[ "-wc", "t mV mV mV mV", ""],
	["-wc1", "t mV mV mV mV", ""],
	["-wc2", "t mV mV mV mV", "Where t (type) is \x1b[1m1\x1b[0m for rechargeable batteries, \x1b[1m2\x1b[0m for non-rechargeable\n    batteries, \x1b[1m3\x1b[0m for PSU / DC adapter, and the four mV settings are the\n    \x1b[1mmaximum\x1b[0m, \x1b[1mgood\x1b[0m, \x1b[1mlow\x1b[0m, and \x1b[1mcritical\x1b[0m voltage levels."
],
	["-won", "seconds", "This timer counts down from the moment the MoPi is powered off and turns it\n    back on when reaching zero."],
	["-wsd", "seconds", "This timer counts down from the moment it is set and powers off the MoPi\n    when reaching zero."],
	]

# create command arrays for use in the code later
commands = []
parameters = []
for i in range(0, len(commands2)):
	commands.append(commands2[i][0])
	parameters.append(commands2[i][3])

# === helper functions start here === #

def shorthelp():
	print "Usage: mopicli [-h] [options]"
	print "This tool is for interfacing with the MoPi battery power add-on"
	print "  board for the Raspberry Pi. (http://pi.gate.ac.uk)"
	print "Package Version %s" % (VERSION)
	print "Version %0.1f, API %0.1f" % (CLIVERSION, mopiapi.getApiVersion())

def help():
	for i in range(0, len(categories)):
		print "%s:" % categories[i]
		for j in range(0, len(commands2)):
			if commands2[j][4] == i: # in the category
				if commands2[j][3] == 0:
					print "  %s\t%s" % (commands2[j][0], commands2[j][1])
				else:
					for z in range(0, len(parameters)):
						if parameters2[z][0] == commands2[j][0]:
							break
					print "  %s %s\t%s" % (commands2[j][0], parameters2[z][1], commands2[j][1])
					if parameters2[z][2] != "":
						print "    %s" % parameters2[z][2]
		print ""

	print "All options may be used simultaneously. When doing so, they execute in the"
	print "  order they are listed in in this help message. However, only one of each may"
	print "  be used."

# read integer inputs to command c, with expected number of inputs
# if none are expected and there are some though, no biggy
def readInts(c, expected):
	ci = sys.argv.index(c) + 1
	data = []
	while ci < len(sys.argv):
		try:
			data.append(int(sys.argv[ci]))
		except:
			break
#		sys.argv.pop(ci)
		ci += 1
	if expected > 0 and len(data) != expected:
		print "mopicli. Bad input for " + c + "."
		sys.exit(1)
	return data

def getLabel(flag):
	if labels == 0:
		return ""
	for j in range(0, len(commands2)):
		if commands2[j][0] == flag:
			l = commands2[j][2]
			if l == '-':
				l = commands2[j][1]
			return "%s: " % l

def confDisp(cnf):
	if cnf[0] == 1:
		print "  Source type: Rechargeable batteries"
	elif cnf[0] == 2:
		print "  Source type: Non-rechargeable batteries"
	elif cnf[0] == 3:
		print "  Source type: PSU / DC adapter"
	print "  Maximum voltage: %i mV" % cnf[1]
	print "  Good voltage: %i mV" % cnf[2]
	print "  Low voltage: %i mV" % cnf[3]
	print "  Critical voltage: %i mV" % cnf[4]

# === code starts here === #

if len(sys.argv) == 1:
	shorthelp()
	sys.exit(0)

# special handling for -h
if '-h' in sys.argv:
	shorthelp()
	print ""
	help()
	sys.exit(0)

sys.argv.pop(0) # remove calling name...
# check that all the commands given are valid
for arg in sys.argv:
	if not arg.isdigit() and not arg in commands:
		print "mopicli. Unknown option: " + arg
		sys.exit(1)

if mopiapi.getApiVersion() != APIVERSION:
	print "mopicli. Expected API version %0.1f, got %0.1f instead." % (APIVERSION, mopiapi.getApiVersion())
	sys.exit(1)

# special handling for -e
if '-e' in sys.argv:
	sys.argv.extend(['-fv', '-sn', '-s', '-sv', '-v', '-v1', '-v2', '-rc', '-rc1', '-rc2', '-ron', '-rsd'])

# special handling for -nl
if '-nl' in sys.argv:
	labels = 0
else:
	labels = 1

status = 0
try:
	# special handling for -i
	if '-i' not in sys.argv:
		mymopi = mopiapi.mopiapi()

	# find out about the battery configuration
	diff = 1 # differing by default
	if '-rc' in sys.argv or '-rc1' in sys.argv or '-rc2' in sys.argv:
		cnf0 = mymopi.readConfig()
		for z in range(0, len(cnf0)):
			if cnf0[z] != 255:
				diff = 0
				break
		# if the config doesn't differ, then display that for -rc1/-rc2 when there's no -rc
		if diff == 0:
			if not '-rc' in sys.argv and ('-rc1' in sys.argv or '-rc2' in sys.argv):
				sys.argv.append('-rc')
				sys.argv = [value for value in sys.argv if (value != '-rc1' and value != '-rc2')]
		else: # do the opposite and show the difference config with -rc when it's called
			if '-rc' in sys.argv:
				sys.argv.append('-rc1')
				sys.argv.append('-rc2')

	# check if each command exists in the arguments array, and process
	# doing it this way around keeps the output in a fixed order
	for ind,arg in enumerate(commands):
		if arg in sys.argv:
			par = readInts(arg, parameters[ind])

			if '-i' == arg:
				mymopi = mopiapi.mopiapi(par[0])

			if '-fv' == arg:
				data = mymopi.getFirmwareVersion()
				print '%s%i.%02i' % (getLabel(arg), data[0], data[1])

			if '-sn' == arg:
				print "%s%i" % (getLabel(arg), mymopi.getSerialNumber())


			if '-s' == arg:
				status = mopiapi.status(mymopi.getStatus())
				print "%s%i" % (getLabel(arg), status.getByte())

			if '-sv' == arg:
				if status == 0:
					status = mopiapi.status(mymopi.getStatus())
				if labels:
					print getLabel(arg)
					print "  " + status.StatusDetail().replace("\n", "\n  ")
					
				else:
					print status.StatusDetail()

			if '-v' == arg:
				print "%s%i" % (getLabel(arg), mymopi.getVoltage())
			if '-v1' == arg:
				print "%s%i" % (getLabel(arg), mymopi.getVoltage(1))
			if '-v2' == arg:
				print "%s%i" % (getLabel(arg), mymopi.getVoltage(2))


			if '-rc' == arg:# or ('-rc1' in sys.argv and diff == 0) or ('-rc2' in sys.argv and diff == 0):
				if labels == 1:
					if diff:
						print "%sdiffering" % getLabel(arg)
					else:
						print getLabel(arg)
						confDisp(cnf0)
				else:
					print ' '.join(map(str, cnf0))

			if '-rc1' == arg:
				if labels == 1:
					if diff == 1 or (diff != 0 and '-rc' in sys.argv):
						cnf = mymopi.readConfig(1)
						print getLabel(arg)
						confDisp(cnf)
					else:
						print "%smatching, see above" % getLabel(arg)
				else:
					cnf = mymopi.readConfig(1)
					print ' '.join(map(str, cnf))
			# same logic as above
			if '-rc2' == arg:
				if labels == 1:
					if diff == 1 or (diff != 0 and '-rc' in sys.argv):
						cnf = mymopi.readConfig(2)
						print getLabel(arg)
						confDisp(cnf)
					else:
						print "%smatching, see above" % getLabel(arg)
				else:
					cnf = mymopi.readConfig(2)
					print ' '.join(map(str, cnf))

			if '-ron' == arg:
				d = mymopi.getPowerOnDelay()
				if labels == 1:
					print "%s%i seconds" % (getLabel(arg), d)
				else:
					print d

			if '-rsd' == arg:
				d = mymopi.getShutdownDelay()
				if labels == 1:
					print "%s%i seconds" % (getLabel(arg), d)
				else:
					print d


			if '-wc' == arg:
				mymopi.writeConfig(par)
			if '-wc1' == arg:
				mymopi.writeConfig(par, 1)
			if '-wc2' == arg:
				mymopi.writeConfig(par, 2)

			if '-won' == arg:
				mymopi.setPowerOnDelay(par[0])

			if '-wsd' == arg:
				mymopi.setShutdownDelay(par[0])

except IOError as e:
	if e.errno == errno.EACCES:
		print "mopicli. Permission denied. Are you running as root?"
	elif e.errno == errno.EIO:
		print "mopicli. %s. Check bus? Check connection?" % e.strerror
	elif e.errno == errno.ECOMM:
		print "mopicli. %s." % e.strerror
		sys.exit(2)
	else:
		print "mopicli. " + e.strerror
	sys.exit(1)
except OSError as e:
	print "mopicli. " + e.strerror
	sys.exit(1)
