#!/usr/bin/python
#
# Python based eeprom interface for Mercury H3600 Backpaq project
# Copyright 2001 Compaq Computer Corporation.
#
# Use consistent with the GNU GPL is permitted,
# provided that this copyright notice is
# preserved in its entirety in all copies and derived works.
#
# COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
# AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
# FITNESS FOR ANY PARTICULAR PURPOSE.
#                   
# Author: Andrew Christian
#         <andrew.christian@compaq.com>
#
#  Bugs to fix:
#
#   * Add an "About" box

import sys
import os
import string
import re
from gtk import *
import GtkExtra


class eeitem:
    def __init__(self,value,procloc):
        self.value = value
        self.procloc = procloc
        self.default = value

eepromProcGen  = "/proc/backpaq/eeprom"
eepromProcBase = "/proc/sys/backpaq/eeprom/"

eepromGood   = FALSE   # Valid eeprom
eepromSetAll = FALSE   # Default EEPROM values set
    

eepromValues = {
    'major_revision' : eeitem(2, "major"),
    'minor_revision' : eeitem(5, "minor"),
    'fpga_version'   : eeitem(0, "fpga"),
    'camera'         : eeitem(0x82, "camera"),
    'accel_offset_x' : eeitem(0x8000, "accel/xoffset"),
    'accel_scale_x'  : eeitem(0x8000, "accel/xscale"),
    'accel_offset_y' : eeitem(0x8000, "accel/yoffset"),
    'accel_scale_y'  : eeitem(0x8000, "accel/yscale"),
    'serial_number'  : eeitem(1, "serial"),
    'flash_base'     : eeitem(0x00000000, "flashstart"),
    'flash_length'   : eeitem(0x02000000, "flashlen"),
    'sysctl_base'    : eeitem(0x02000000, "sysctlstart")
    }

class menuEntry:
    def __init__(self,name,value):
        self.name = name
        self.value = value

cameraItems = [ menuEntry("SMaL Original Grey", 0),
                menuEntry("SMaL Original Color", 1),
                menuEntry("SMaL Release Color", 2),
                menuEntry("SMaL Autobright 0.9", 3) ]
        
fpgaItems = [   menuEntry("Virtex 100",0),
                menuEntry("Virtex 100E",1),
                menuEntry("Virtex 200E",2),
                menuEntry("Virtex 300E",3) ]

#######################################################

def mainfunc(windowMainWidget):
    windowMainWidget.windowMain.connect("delete_event", mainquit)

def on_buttonRevert_clicked(widget, mainObj):
    load_eeprom()
    mainObj.update_values()
    mainObj.mark_clean()

def on_buttonProgram_clicked(widget, mainObj):
    mainObj.extract_values()
    store_eeprom()
    load_eeprom()    # For paranoia's sake, we reload from EEPROM
    mainObj.update_values()
    mainObj.mark_clean()

def on_buttonDefault_clicked(widget, mainObj):
    for item in eepromValues.keys():
        eepromValues[item].value = eepromValues[item].default

    global eepromGood, eepromSetAll
    print "Current eepromGood ", eepromGood
    foo = eepromSetAll
    print "Current value of eepromSetAll is ", eepromSetAll
    eepromSetAll = TRUE
    mainObj.update_values()
    mainObj.mark_dirty()

def on_optionmenuFPGA(widget, item):
    "Called when an FPGA menu item is selected"
    eepromValues['fpga_version'].value = item.value
    mainWindow.mark_dirty()

def on_optionmenuCamera(widget, item):
    "Called when an FPGA menu item is selected"
    # Note that this may not correctly reflect the Landscape setting
    eepromValues['camera'].value = item.value
    mainWindow.mark_dirty()


def load_eeprom():
    "Load data from the proc file system and store it in eepromValues"
    if ( os.access(eepromProcGen, os.R_OK)):
        f = open(eepromProcGen,"r")
        line = f.readline()
        f.close()
        # Check for a valid EEPROM
        if ( string.find(string.lower(line),"okay")>=0):
            for item in eepromValues.keys():
                dest = eepromProcBase + eepromValues[item].procloc
                if ( os.access(dest, os.R_OK)):
                    f = open(dest,"r")
                    eepromValues[item].value = eval(f.readline())
                    f.close()
                
def store_eeprom():
    "Poke into the proc file system"
    global eepromGood, eepromSetAll
    if eepromGood:
        if eepromSetAll:
            update_list = eepromValues.keys()
        else:
            update_list = ('major_revision', 'minor_revision',
                     'fpga_version', 'camera', 'serial_number')
            
        for item in update_list:
            print "Handling ", item, " value ", eepromValues[item]
            dest = eepromProcBase + eepromValues[item].procloc
            if ( os.access(dest, os.W_OK)):
                f = open(dest,"w")
                f.write(("%d" % eepromValues[item].value))
                f.close()
        eepromSetAll = FALSE
    else:
        print "Attempting to store by eeprogram"
        cmd = "eecreate --major=" + str(eepromValues['major_revision'].value) + \
              " --minor=" + str(eepromValues['minor_revision'].value) + \
              " --camera=" + str(eepromValues['camera'].value & 0x7f) + \
              " --fpga=" + str(eepromValues['fpga_version'].value)

        if ( eepromValues['camera'].value & 0x80):
            cmd += " --landscape"

        cmd += " " + str(eepromValues['serial_number'].value) + " | eeprogram"
        os.system(cmd)
        displayMessage("Reboot your iPAQ now")
        mainquit()

def makeButton( name, mainObj, callback ):
    "Build a single button"
    btn=GtkButton(name)
    btn.set_flags(CAN_FOCUS)
    btn.set_usize(-1, -1)
    btn.connect("clicked", callback, mainObj)
    btn.GTKPY_MAIN = mainObj
    btn.show()
    return btn

def makeLabel(text):
    "Build a single label widget and install it in a table"
    myLabel = GtkLabel()
    myLabel.set_usize(-1, -1)
    myLabel.set_text(text)
    myLabel.set_justify(JUSTIFY_LEFT)
    myLabel.set_alignment(0, 0.5)
    myLabel.set_padding(0, 0)
    myLabel.show()
    return myLabel

def makeSpin(inValue, inMin, inMax):
    "Build a spin button"
    spin = GtkSpinButton(GtkAdjustment(inValue, inMin, inMax,1,10,10), 1, 0)
    spin.set_flags(CAN_FOCUS)
    spin.set_usize(-1, -1)
    spin.set_numeric(FALSE)
    spin.set_update_policy(UPDATE_ALWAYS)
    spin.set_editable(TRUE)
    spin.set_visibility(TRUE)
    spin.set_text("%d" % inValue)
    spin.connect("changed",
                 (lambda widget, data : mainWindow.mark_dirty()),
                 spin )
    spin.show()
    return spin

def makeOptionMenu( menuItems, callback ):
    "Build an option menu with the specified menu items and an initial value"
    myMenu = GtkOptionMenu()
    myMenu.set_flags(CAN_FOCUS)
    myMenu.set_usize(-1, -1)

    theMenu = GtkMenu()
    theMenuList = []
    for item in menuItems:
        aMenuItem = GtkMenuItem(item.name)
        aMenuItem.connect("activate", callback, item)
        theMenuList.append(item)
        theMenu.append(aMenuItem)
        aMenuItem.show()

    myMenu.menulist = theMenuList
    myMenu.set_menu(theMenu)
    myMenu.show()
    return myMenu

def dlg_display_click(widget,mainObj):
    "Called when the dialog button is pushed"
    mainObj.destroy();

def dlg_destroy(widget, mainObj):
    "Called when the dialog is destroyed"
    widget.hide()
    mainquit()
    
def displayMessage(text):
    dlg = new( GtkDialog,
               type=WINDOW_TOPLEVEL,
               title="Warning!",
               allow_grow=TRUE,
               allow_shrink=FALSE,
               border_width=10 )
    label = makeLabel(text)
    dlg.vbox.pack_start(label,FALSE,FALSE,0)
    btn = makeButton("Okay", dlg, dlg_display_click )
    dlg.action_area.pack_start(btn,FALSE, FALSE, 0)
    dlg.set_modal(TRUE)
    dlg.connect("destroy", dlg_destroy, 0)

    dlg.show()
    mainloop()
    

class windowMainWidget:
    def mark_dirty( self ):
        self.dirty = TRUE
        self.buttonProgram.set_sensitive(TRUE)
        self.buttonRevert.set_sensitive(TRUE)

    def mark_clean( self ):
        self.dirty = FALSE
        self.buttonProgram.set_sensitive(FALSE)
        self.buttonRevert.set_sensitive(FALSE)

    def update_values( self ):
        "Update the visible widgets to match the eepromValues dictionary"
        self.myWidgets['spinMajor'].set_value( eepromValues['major_revision'].value )
        self.myWidgets['spinMinor'].set_value( eepromValues['minor_revision'].value )
        self.myWidgets['spinSerialNumber'].set_value( eepromValues['serial_number'].value )
        self.myWidgets['optionmenuCamera'].set_history( eepromValues['camera'].value & 0x7f )
        self.myWidgets['checkLandscape'].set_active( eepromValues['camera'].value & 0x80 )
        self.myWidgets['optionmenuFPGA'].set_history( eepromValues['fpga_version'].value )
        self.myWidgets['accel_offset_x'].set_text( "0x%04x" % eepromValues['accel_offset_x'].value )
        self.myWidgets['accel_offset_y'].set_text( "0x%04x" % eepromValues['accel_offset_y'].value )
        self.myWidgets['accel_scale_x'].set_text( "0x%04x" % eepromValues['accel_scale_x'].value )
        self.myWidgets['accel_scale_y'].set_text( "0x%04x" % eepromValues['accel_scale_y'].value )
        self.myWidgets['flash_base'].set_text( "0x%08x" % eepromValues['flash_base'].value )
        self.myWidgets['flash_length'].set_text( "0x%08x" % eepromValues['flash_length'].value )
        self.myWidgets['sysctl_base'].set_text( "0x%08x" % eepromValues['sysctl_base'].value )

    def extract_values( self ):
        eepromValues['major_revision'].value = self.myWidgets['spinMajor'].get_value_as_int()
        eepromValues['minor_revision'].value = self.myWidgets['spinMinor'].get_value_as_int()
        eepromValues['serial_number'].value  = self.myWidgets['spinSerialNumber'].get_value_as_int()
        # The camera index has been set by the option menu callback, but the
        # state of the landscape mode is probably old
        cam = eepromValues['camera'].value & 0x7f
        if self.myWidgets['checkLandscape'].get_active():
            cam |= 0x80
        eepromValues['camera'].value = cam

    def __init__(self):
        self.myWidgets = {}   # Storage for all widgets we need to access
        self.dirty = 0
        
        windowMain=GtkWindow(WINDOW_TOPLEVEL)
        windowMain.set_title("H3600 Backpaq EEPROM")
        windowMain.set_usize(-1, -1)
        windowMain.set_policy(FALSE, FALSE, FALSE)
        windowMain.set_position(WIN_POS_NONE)

        vbox1=GtkVBox()
        windowMain.add(vbox1)
        vbox1.set_usize(-1, -1)
        vbox1.set_homogeneous(FALSE)
        vbox1.set_spacing(0)

        table3=GtkTable(10, 2)
        vbox1.pack_start(table3, FALSE, FALSE, 0)
        table3.set_usize(-1, -1)
        table3.set_row_spacings(2)
        table3.set_col_spacings(4)

        leftLabels = ["Version:", "Serial #:", "FPGA:", "Camera:","",
                      "Offset:", "Scale:", "Flash base:", "Flash len:",
                      "Sysctl base:" ]

        for i in range(len(leftLabels)):
            label = makeLabel(leftLabels[i])
            table3.attach(label, 0, 1, i, i + 1, FILL, 0, 0, 0)

        hbox1=GtkHBox()
        table3.attach(hbox1, 1, 2, 0, 1, EXPAND|FILL, FILL, 0, 0)
        hbox1.set_usize(-1, -1)
        hbox1.set_homogeneous(FALSE)
        hbox1.set_spacing(0)

        spinMajor = makeSpin(0, 0, 255)
        hbox1.pack_start(spinMajor,FALSE,TRUE,0)
        self.myWidgets['spinMajor'] = spinMajor

        spinMinor = makeSpin(0, 0, 255)
        hbox1.pack_start(spinMinor,FALSE,TRUE,0)
        self.myWidgets['spinMinor'] = spinMinor

        hbox1.show()
        self.hbox1=hbox1

        spinSerialNumber = makeSpin(1,1,0xfffffff)
        table3.attach(spinSerialNumber,1,2,1,2,FILL,0,0,0)
        self.myWidgets['spinSerialNumber'] = spinSerialNumber
        
        hbox2=GtkHBox()
        table3.attach(hbox2, 1, 2, 3, 4, FILL, 0, 0, 0)
        hbox2.set_usize(-1, -1)
        hbox2.set_homogeneous(FALSE)
        hbox2.set_spacing(3)

        optionmenuCamera = makeOptionMenu(cameraItems, on_optionmenuCamera)
        hbox2.pack_start(optionmenuCamera, FALSE, FALSE, 0)
        self.myWidgets['optionmenuCamera'] = optionmenuCamera
        hbox2.show()
        self.hbox2=hbox2

        hbox4=GtkHBox()
        table3.attach(hbox4, 1, 2, 2, 3, FILL, FILL, 0, 0)
        hbox4.set_usize(-1, -1)
        hbox4.set_homogeneous(FALSE)
        hbox4.set_spacing(0)
        
        optionmenuFPGA=makeOptionMenu(fpgaItems, on_optionmenuFPGA)
        hbox4.pack_start(optionmenuFPGA, FALSE, FALSE, 0)
        self.myWidgets['optionmenuFPGA']=optionmenuFPGA

        hbox4.show()
        self.hbox4=hbox4

        table4=GtkTable(2, 4)
        table3.attach(table4, 1, 2, 5, 7, FILL, 0, 0, 0)
        table4.set_usize(-1, -1)
        table4.set_row_spacings(2)
        table4.set_col_spacings(8)
        
        table4.attach(makeLabel("X:"),0,1,0,1,FILL, 0, 0, 0)
        table4.attach(makeLabel("X:"),0,1,1,2,FILL, 0, 0, 0)
        table4.attach(makeLabel("Y:"),2,3,0,1,FILL, 0, 0, 0)
        table4.attach(makeLabel("Y:"),2,3,1,2,FILL, 0, 0, 0)

        label = makeLabel("")
        table4.attach(label,1,2,0,1,FILL,0,0,0)
        self.myWidgets['accel_offset_x'] = label

        label = makeLabel("")
        table4.attach(label,3,4,0,1,FILL,0,0,0)
        self.myWidgets['accel_offset_y'] = label

        label = makeLabel("")
        table4.attach(label,1,2,1,2,FILL,0,0,0)
        self.myWidgets['accel_scale_x'] = label

        label = makeLabel("")
        table4.attach(label,3,4,1,2,FILL,0,0,0)
        self.myWidgets['accel_scale_y'] = label
        table4.show()
        
        index = 7
        for i in ('flash_base','flash_length','sysctl_base'):
            label = makeLabel("")
            table3.attach(label, 1, 2, index, index + 1, FILL, 0, 0, 0)
            self.myWidgets[i] = label
            index+=1

        hbox5=GtkHBox()
        table3.attach(hbox5, 1, 2, 4, 5, FILL, EXPAND|FILL, 0, 0)
        hbox5.set_usize(-1, -1)
        hbox5.set_homogeneous(FALSE)
        hbox5.set_spacing(0)

        checkLandscape=GtkCheckButton("Landscape")
        hbox5.pack_start(checkLandscape, FALSE, FALSE, 0)
        checkLandscape.set_flags(CAN_FOCUS)
        checkLandscape.set_usize(-1, -1)
        checkLandscape.connect("toggled", (lambda widget, data : mainWindow.mark_dirty()), checkLandscape )
        checkLandscape.show()
        self.myWidgets['checkLandscape']=checkLandscape

        hbox5.show()
        self.hbox5=hbox5

        table3.show()
        self.table3=table3

        hseparator1=GtkHSeparator()
        vbox1.pack_start(hseparator1, TRUE, TRUE, 3)
        hseparator1.set_usize(-1, -1)
        hseparator1.show()
        self.hseparator1=hseparator1

        hbox3=GtkHBox()
        vbox1.pack_start(hbox3, TRUE, TRUE, 0)
        hbox3.set_usize(-1, -1)
        hbox3.set_homogeneous(TRUE)
        hbox3.set_spacing(0)
        
        buttonDefault = makeButton("Default", self, on_buttonDefault_clicked )
        hbox3.pack_start(buttonDefault, FALSE, TRUE, 0)
        self.buttonDefault = buttonDefault

        buttonRevert = makeButton( "Reload", self, on_buttonRevert_clicked )
        hbox3.pack_start(buttonRevert, FALSE, TRUE, 0)
        self.buttonRevert = buttonRevert

        buttonProgram = makeButton( "Program", self, on_buttonProgram_clicked )
        hbox3.pack_start(buttonProgram, FALSE, TRUE, 0)
        self.buttonProgram=buttonProgram

        hbox3.show()
        self.hbox3=hbox3

        vbox1.show()
        self.vbox1=vbox1
        windowMain.show()
        self.windowMain=windowMain


if __name__ == '__main__':
    # First check for a valid EEPROM
    if ( os.access(eepromProcGen, os.R_OK)):
        f = open(eepromProcGen, "r")
        line = f.readline()
        f.close()
        if ( string.find(string.lower(line),"okay") >= 0):  # Valid EEPROM
            eepromGood = TRUE
        else: # Invalid EEPROM - must program first
            displayMessage("Your EEPROM does not appear to \n"\
                           "have been programmed.  You may\n"\
                           "program it once through the interface,\n"\
                           "but you will have to reboot your\n"\
                           "machine.")

        mainWindow=windowMainWidget()

        if eepromGood:
            load_eeprom()
            mainWindow.mark_clean()

        mainWindow.update_values()

        mainfunc(mainWindow)
        mainloop()
    else:
        # Invalid world, must request backpaq first
        displayMessage("I can't locate a valid Backpaq\n"\
                       "Exiting program")
