#$Id: psionSpr.py,v 1.1 1996/09/23 04:25:15 connolly Exp $
#
# Documentation on psion spreadsheet format at:
# PSIONICS FILE - SPR.FMT
# =======================
# Format of Spreadsheet files
# Last modified 1994-03-01
# ===========================
# http://www.cityscape.co.uk/~cdwf/psion/psionics/spr.fmt

import struct
import psionData
from psionData import readStruct, readQstr

class Sheet(psionData.T):
    def __init__(self):
	self.formulas = []
	self.rows = {}
	self.maxRow = 0
	self.maxCol = 0

    def factory(self, ty):
	if ty == 1:
	    return Formula
	if ty == 2:
	    return Cell
	else:
	    return psionData.Record

    def addCell(self, row, col, cell):
#	print "@#addCell: ", row, col, cell.value

	if not self.rows.has_key(row):
	    self.rows[row] = {}

	if row > self.maxRow:
	    self.maxRow = row
	if col > self.maxCol:
	    self.maxCol = col

	self.rows[row][col] = cell

    def addFormula(self, f):
	self.formulas.append(f)
	#print "@#addFormula #: ", len(self.formulas)

    rec_fmt = 'hh'

    def loadRecords(self, stream):
	qty = struct.calcsize(self.rec_fmt)
	while 1:
	    h = stream.read(qty)
	    if not h: break
	    (ty, siz) = struct.unpack(self.rec_fmt, h)

	    siz = siz & 0x0FFF #@@ handle flag bits

	    f = self.factory(ty)
	    r = f(self, ty, siz)
	    r.load(stream)

    def rowRange(self, row, startCol, width=1, empty=None):
	l = [empty] * width

	try:
	    r = self.rows[row]
	    for h in range(0, width):
		try:
		    l[h] = r[startCol + h].value
		except KeyError:
		    pass
	except KeyError:
	    pass

	return tuple(l)
	    
class Cell(psionData.Record):
    def __init__(self, parent, ty, siz):
	self.siz = siz
	self.sheet = parent
	pass

    reffmt = 'hh'
    rangefmt = 'hhhh'
    realfmt = 'd'
    wordfmt = 'h'
    fmt = reffmt + 'bb'

    def load(self, stream):
	(self.col, self.row, \
	 flags, format) = readStruct(self.fmt, stream)

	qty = self.loadValue(stream, flags & 7)

	pad = self.siz - (struct.calcsize(self.fmt) + qty)
	if pad > 0:
#	    print "@# cell pad bytes: ", pad
	    stream.read(pad)
	    #@# handle font byte
	elif pad < 0:
	    raise IOError, 'Goofy cell size! ' + `pad`

	if self.value:
	    self.sheet.addCell(self.row, self.col, self)

    def loadValue(self, stream, ty):
	if ty == 0:
	    qty = 0
	    self.value = None
	elif ty == 1:
	    qty = struct.calcsize(self.realfmt)
	    (self.value,) = readStruct(self.realfmt, stream)
	elif ty == 2:
	    self.value = readQstr(stream)
	    qty = len(self.value) + 1
	elif ty == 3:
	    qty = struct.calcsize(self.wordfmt)
	    (self.value,) = readStruct(self.wordfmt, stream)
	elif ty == 5:
	    # bummer! python struct module does alignment...
	    (self.fomulaIndex,) = readStruct('h', stream)
	    (self.value,) = readStruct('d', stream)
	    qty = struct.calcsize('h') + struct.calcsize('d')
	else:
	    raise IOError, "@@unknown cell data format" + `ty`

	return qty

class Formula(psionData.Record):
    def __init__(self, parent, ty, siz):
	self.sheet = parent

    fmt = 'hb'

    def load(self, stream):
	(self.refCount, qty) = readStruct(self.fmt, stream)

	# @@ formula data
	stream.read(qty)
	self.sheet.addFormula(self)


# test harness

def test():
    import sys

    s = Sheet()
    s.load(sys.stdin)

    for r in range(0, s.maxRow):
	print s.rowRange(r, 0, 7)

if __name__ == '__main__': test()
