#!/usr/bin/python -tt
import warnings
import traceback
import subprocess
import time
import sys
import re

import iguanaIR


CMD_CLEAR  = '\x01'
CMD_HOME   = '\x02'
CMD_SCROLL = '\x07'
CMD_DISPLAY_OFF = '\x08'
CMD_CURSOR_OFF = '\x0C'
CMD_DISPLAY_OFF = '\x08'




POWER_PIN  = 0x08
REGSEL_PIN = 0x04
RW_PIN     = 0x02
ENABLE_PIN = 0x01




#output "constants"
LOG_FATAL  = 0
LOG_ERROR  = 1
LOG_WARN   = 2
LOG_ALWAYS = 2.5
LOG_NORMAL = 3
LOG_INFO   = 4
LOG_DEBUG  = 5

msgPrefixes = [
    "FATAL: ",
    "ERROR: ",
    "WARNING: ",
    "",
    "INFO: ",
    "DEBUG: "
]

null = open('/dev/null', 'r+')

#local variables
currentLevel = LOG_NORMAL
logFile = None
text = None
width = 16
gap = 2
shouldScroll = False

def dieCleanly(level = None):
    """Exit the application with proper cleanup."""

    #TODO: perform application cleanup

    if level == None:
        level = LOG_ERROR

    #exit with appropriate value
    if level == LOG_FATAL:
        sys.exit(1)
    sys.exit(0)


def message(level, msg):
    """Print a message to a certain debug level"""
    retval = None

    if level <= currentLevel or level == LOG_ALWAYS:
        out = sys.stdout

        # if logfile is open print to it instead
        if logFile == "-":
            out = sys.log
        elif level <= LOG_WARN:
            out = sys.stderr

        retval = msgPrefixes[int(level + 0.5)] + msg
        out.write(retval)
        retval = len(retval)

    if level <= LOG_FATAL:
        dieCleanly(level)

    return retval


def printUsage(msg = None):
    usage = "Usage: " + sys.argv[0] + " [OPTION]..." + """

-h
--help : Print this usage message.

-l
--log-file : Specify a log to receive all messages.

-q
--quiet : Decrease verbosity.

-v
--verbose : Increase verbosity.
"""

    if msg != None:
        message(LOG_FATAL, msg + usage)
    message(LOG_ALWAYS, usage)
    dieCleanly(LOG_ALWAYS)


index = 1
while index < len(sys.argv):
    arg = sys.argv[index]
    if arg == "-h" or arg == "--help":
        printUsage()
    elif arg == "-l" or arg == "--log-file":
        index += 1
        logFile = sys.argv[index]
        if logFile == "-":
            logFile = None
    elif arg == "-q" or arg == "--quiet":
        if currentLevel > LOG_FATAL:
            currentLevel -= 1
    elif arg == "--scroll":
        shouldScroll = True
    elif arg == "-v" or arg == "--verbose":
        currentLevel += 1
    elif text is None:
        text = arg
    else:
        text += ' ' + arg
    index += 1

# open the log file if specified
if logFile != None:
    sys.log = open(logFile, "a", 1)
    logFile = "-"

_conn = iguanaIR.connect('0')

def deviceTransaction(type, data = '', quiet = False):
    retval = False
    req = iguanaIR.createRequest(type, data)
    if not iguanaIR.writeRequest(req, _conn):
        if not quiet:
            print 'Failed to write packet. %s\n' % _conn
    else:
        resp = iguanaIR.readResponse(_conn, 3000)
        if resp is None:
            if not quiet:
                print "No response received.\n"
        elif type == iguanaIR.IG_DEV_GETVERSION:
            if not iguanaIR.responseIsError(resp):
                data = iguanaIR.removeData(resp)
                retval = ord(data[0]) + (ord(data[1]) << 8)
        elif iguanaIR.responseIsError(resp):
            if not quiet:
                print 'Error response code: 0x%s\n' % iguanaIR.code(resp)
        else:
            retval = iguanaIR.removeData(resp)

    return retval

def strobeEnable(pins):
    pins |= ENABLE_PIN | POWER_PIN
    deviceTransaction(iguanaIR.IG_DEV_SETPINS,
                      chr(pins & 0x0F) + chr((pins & 0xF0) >> 4))
    pins &= ~ENABLE_PIN
    deviceTransaction(iguanaIR.IG_DEV_SETPINS,
                      chr(pins & 0x0F) + chr((pins & 0xF0) >> 4))

def writeString(text):
    for c in text:
        # write high nibble)
        strobeEnable((ord(c) & 0xF0) | REGSEL_PIN)
        # write low nibble)
        strobeEnable(((ord(c) & 0x0F) << 4) | REGSEL_PIN)

def writeBulkPinData(data):
    if version < 2:
        raise Exception('First bulkpin support was in version 2.')
    deviceTransaction(iguanaIR.IG_DEV_PINBURST, data)

def generateBulkPinData(text, isCommand = True):
    if (version == 2 and len(text) > 15) or \
       len(text) > 35:
        raise ValueError('Text too long')

    regsel = REGSEL_PIN
    if isCommand:
        regsel = 0

    output = ''
    if version == 2:
        output = chr(len(text) * 4)
    for c in text:
        a = (ord(c) & 0xF0)
        b = (ord(c) & 0x0F) << 4
        output += chr(a | regsel | POWER_PIN | ENABLE_PIN) + \
                  chr(a | regsel | POWER_PIN) + \
                  chr(b | regsel | POWER_PIN | ENABLE_PIN) + \
                  chr(b | regsel | POWER_PIN);
    if version == 2:
        output += chr(0) * (64 - len(output))
    return output

def bulkWriteCommands(text, isCommand = True, postDelay = 0):
    writeBulkPinData(generateBulkPinData(text, isCommand))
    if postDelay:
        time.sleep(postDelay)

def prepareLine(line):
    # figure out how much to scroll
    length = len(line)
    if length < width:
        length = width
    length += gap
    if length > 40:
        length = 40

    return line + (length - len(line)) * ' '

def writeLoop(lines, x, pos, cursor, delay):
    if cursor[x] >= len(lines[x]):
        # move to the position right after the text (off screen)
        bulkWriteCommands(chr(0x80 + 0x40 * x + cursor[x] % 40))
        bulkWriteCommands(lines[x][pos[x]], False)
        pos[x] = (pos[x] + 1) % len(lines[x])
    cursor[x] += 1

    return (pos, cursor)

def scroll(lines, delay = 0.25):
    cursor = []
    pos = []
    for x in range(len(lines)):
        lines[x] = prepareLine(lines[x])
        cursor.append(len(lines[x]))
        pos.append(0)

    while True:
        for x in range(len(lines)):
            (pos, cursor) = writeLoop(lines, x, pos, cursor, delay)
        bulkWriteCommands('\x18', postDelay = delay)

def bulkWriteString(text):
    lines = text.split('\\n')
    if len(lines) > 2:
        message(LOG_ERROR, "Too many lines passed for current display.\n")
    else:
        for x in range(len(lines)):
            if len(lines[x]) > 35:
                message(LOG_ERROR,
                        "Line is longer than 35 characters.  Truncating.\n")
                lines[x] = lines[x][:35]

        bulkWriteCommands(CMD_CLEAR)
        bulkWriteCommands(CMD_CURSOR_OFF)

        bulkWriteCommands(lines[0], isCommand = False, postDelay = 0.25)
        if len(lines) > 1:
            bulkWriteCommands('\xC0')
            bulkWriteCommands(lines[1], isCommand = False, postDelay = 0.25)

        if shouldScroll:
            scroll(lines)

def initLCD():
    global version
    # detect the version nice an early
    version = deviceTransaction(iguanaIR.IG_DEV_GETVERSION)

    # set all the pins to outputs
    deviceTransaction(iguanaIR.IG_DEV_SETPINCONFIG,
                      chr(iguanaIR.IG_OUTPUT) * 8)

    # send the Function Set command 3 times
    strobeEnable(0x30)
    strobeEnable(0x30)
    strobeEnable(0x30)

    # set the data length (width) to 4 instead of 8
    strobeEnable(0x20)

    # Function Set, display off, display clear, mode set
    bulkWriteCommands('\x28\x08\x01\x08')
    # sometimes need a slight delay before the first display on
    bulkWriteCommands('\x0F')

initLCD()
if text is None:
    writeString('H')
    bulkWriteString('ello world')
else:
    bulkWriteString(text)
