#!/usr/local/bin/python

# Displays given input string centered on LCD display.
# While simple input (alphanumeric) can be given without
# quotes, safest is to always pass argument with quotes...
#
# Ok    : isi_lcd_display Hello World
# Better: isi_lcd_display "Hello World"
# Bad   : isi_lcd_display Hello World ? What about this
#
# Options:
# Several options are available, which MUST precede the string.
#
# Added a brightness (-i intensity) control for non-screensaver
# mode, to help avoid burnin, when nodes are halted after displaying
# message.  Valid values are 1 (dim) to 8 (bright).
#
# We can add other options:
#
# blink: there is a reverse mode too; is this desirable as an option
# blink: separate option to adjust the period? (currently 1sec)
#
# Really consider making scroll screensaver have no options,
# since its possible to "lose" the LCD for looooooong periods.
#                SCROLL [ SPEED ]     Default SPEED=255 (max)
# Maybe just have options ['slow','medium','fast'] ?
#
# when in screensaver active: reduce brightness
# when disabling screensaver: restore brightness
#
# Add optional args to specify col, row ??

import os, sys, signal, time, getopt
import isi.hw.check.lib.misc as misc
import isi.hw.check.lib.lcd as lcd

from isi.ui.lcd import LCDError

PROGNAME = os.path.basename(sys.argv[0])

class Args:
    usage = \
"""
Usage: %s [OPTIONS] "output string"

OPTIONS:
  -c, --clear            Clear display (ignores other options/output)
  -k, --kill             Auto-kill mcp,lcd daemons as necessary (no prompt)
  -r, --reverse          Reverse output on display
  -i <INTENSITY>         Intensity (Brightness), from 1 (dim) to 8 (bright)
  -b <SECONDS>           Blink output on display for SECONDS
  -x <SCREENSAVER>       Screensaver Type, Default BLINK
                             SCROLL
                             BLINK [ -o PERIOD ]     Default PERIOD=2sec
                             NONE
                         Take care using 'NONE'!! (possible image burnin)
  -o <OPTION>            Screensaver-specific option, See -x
  -t <SECONDS>           Screensaver timeout, Clear screen after SECONDS
                           Default 60 sec
  -h, --help             Show this text

To disable current message in screensaver, issue a --clear command.
If nonzero -t TIMEOUT is specified for screensaver, the display can be
refreshed for additional TIMEOUT seconds simply by hitting any button.

Note that this tool is intended only for 'manufacturing' environments,
due to the killing of the LCD display daemon running in the background.
For non-manufacturing environments, the 'isi_mcp' daemon process will
be running, and the isi_lcd_d daemon will re-start, possibly conflicting
with the running of this script (and screensaver mode).  If necessary,
for safe operation, this script will in fact put the node in safe
manufacturing mode by killing mcp and lcd daemon processes. (Ref --kill)

Only minimal debug on this utility has been done; feedback welcome.
Note the SCROLL screensaver is still in progress.
""" % PROGNAME

    class Error(Exception):
        def __init__(self, *args):
            Exception.__init__(self, *args)
            if len(args) > 1:
                self.exitcode = args[1]
            else:
                # Unix programs generally use 2 for command line syntax errors
                self.exitcode = 2

        # In newer python versions, str() should already be supplied by the
        # BaseException class, but that isn't the case here.  When we go to
        #  > 2.6, see if we can call self.str() instead.
        def message(self):
            """Return the str of 'message'"""
            return str(self.args[0])

        def exitCode(self):
            return self.exitcode

    def __init__(self, args):
        self.output = None
        self.brightness = None
        self.reverse = False
        self.blink = None
        self.clear = False
        self.auto_kill = False
        self.restartLcd = False
        # We dont need, nor want to, restart the isi_lcd_d for Mfg, at least
        self.screensaver_mode = None
        self.screensaver_timeout = None
        self.screensaver_options = None

        try:
            (opts, non) = getopt.getopt(args, 'ckri:b:x:o:t:h',
                                        ['clear','kill','reverse',
                                         'intensity','help'])
        except getopt.GetoptError, e:
            raise Args.Error(str(e))

        for (opt, opt_param) in opts:
            if opt == '-h' or opt == '--help':
                raise Args.Error('', 0)
            elif opt == '-c' or opt == '--clear':
                self.clear = True
                self.output = None
                break # Stop parsing, rest is Do Not Care
            elif opt == '-k' or opt == '--kill':
                self.auto_kill = True
            elif opt == '-r' or opt == '--reverse':
                self.reverse = True
            elif opt == '-i' or opt == '--intensity':
                try:
                    self.brightness = int(opt_param)
                except ValueError:
                    raise Args.Error('invalid -i intensity option: integer exepected')
            elif opt == '-b':
                try:
                    self.blink = int(opt_param)
                except ValueError:
                    raise Args.Error('invalid -b blink option: integer exepected')
            elif opt == '-x':
                self.screensaver_mode = opt_param
            elif opt == '-o':
                self.screensaver_options = opt_param
            elif opt == '-t':
                try:
                    self.screensaver_timeout = int(opt_param)
                except ValueError:
                    raise Args.Error('invalid -t timeout option: integer exepected')
            else:
                raise Args.Error('unsupported option')

        if len(non) > 0:
            self.output = ' '.join(non)

def main(args):
    result = 0
    kb = display = None
    try:
        try:
            kb = misc.KeyboardCheck()
            display = lcd.Lcd(
                args.output, args.brightness,
                args.reverse, args.blink, args.clear,
                args.auto_kill, args.restartLcd,
                args.screensaver_mode, args.screensaver_timeout,
                args.screensaver_options)
            result = display.display()
        except LCDError, e:
            print >> sys.stderr, '%s: LCDError: %s' % (PROGNAME, e)
            result = 1
        except Exception, e:
            print >> sys.stderr, '%s: Unhandled Exception: %s' % (PROGNAME, e)
            result = 1
    finally:
        if kb: kb.close()
        if display: display.close()
    return result

def main_test(args):
    result = 0
    default_pause = 1
    kb = display = None
    def lcd_out(reverse, blink, scroll, saver):
        done = False
        out = 'r%sb%ss%sS%s' % \
              (reverse and 'T' or 'F', blink, scroll, saver and 'T' or 'F')
        display.display(out, reverse, blink, scroll, saver)
        z = kb.check()
        if z and z.lower() == 'q':
            done = True
        else:
            time.sleep(default_pause)
        return done
    done = False
    try:
        kb = misc.KeyboardCheck()
        display = lcd.Lcd(args.output, args.brightness,
                          args.reverse, args.blink, args.clear,
                          args.auto_kill, args.restartLcd,
                          args.screensaver_mode, args.screensaver_timeout,
                          args.screensaver_options)
        for reverse in [False, True]:
            if done: break
            for blink in [0,2,4,8]:
                if done: break
                for scroll in [0,2,4,8]:
                    if done: break
                    for saver in [False, True]:
                        if done: break
                        done = lcd_out(reverse, blink, scroll, saver)

        if display: display.clear()

    finally:
        if kb: kb.close()
        if display: display.close()

    return result
                
if __name__ == '__main__':
    result = 0
    try:
        # ignore ^C
        signal.signal(signal.SIGINT, signal.SIG_IGN)
        try:
            args = Args(sys.argv[1:])
            result = main(args)
            #result = main_test(args)
        except Args.Error, e:
            if e.message():
                print >> sys.stderr, '%s: %s' % (PROGNAME, e.message())
            print >> sys.stderr, Args.usage
            result = e.exitCode()
        except LCDError, e:
            print >> sys.stderr, '%s: LCDError: %s' % (PROGNAME, e)
            result = 1
    finally:
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        sys.exit(result)
