from struct import calcsize, pack, unpack
from types import TupleType, ListType

import numpy as N


# maximum numbers for different sctype
integer_ranges = {
  N.int8:  127.,
  N.uint8: 255.,
  N.int16: 32767.,
  N.uint16: 65535.,  
  # pretty sure these are better than single floating point precision:
  #N.int32: 2147483647.,
  #N.uint32: 4294967295,,
  #N.int64: 9223372036854775807.,
  #N.uint: 18446744073709551615.,
  }


# struct byte order constants
NATIVE = "="
LITTLE_ENDIAN = "<"
BIG_ENDIAN = ">"

# map format chars to python data types
_typemap = dict((
  (("l","L","f","d","q","Q"), float),
  (("h","H","i","I","P"),     int),
  (("x","c","b","B","s","p"), str)))

# All allowed format strings.
allformats = []
for formats in _typemap.keys(): allformats.extend(formats)

format_defaults = {'i': 0,'I':0, 'h': 0, 'H':0, 'f': 0., 'c': '\0',\
                   'l':0.,'s': '', 'B': 0}

######## STRUCT PACKING/UNPACKING/INTERPRETATION ROUTINES ####################
def numvalues(format):
    numstr, fmtchar = format[:-1], format[-1]
    return (numstr and fmtchar not in ("s","p")) and int(numstr) or 1

def elemtype(format):
    fmtchar = format[-1]
    for formats, typ in _typemap.items():
        if fmtchar in formats: return typ
    raise ValueError("format char %s must be one of: %s"%\
                     (fmtchar, allformats))

def sanevalues(format, value):
    nvals, valtype = type(value) in (TupleType, ListType) and \
                     (len(value), type(value[0])) or (1, type(value))
    
    return elemtype(format) == valtype and numvalues(format) == nvals

def formattype(format):
    return numvalues(format) > 1 and list or elemtype(format)

def flatten_values(valseq):
    # flattens the type of header values constructed by aggregate
    if type(valseq) != type([]): return [valseq]
    if valseq == []: return valseq
    return flatten_values(valseq[0]) + flatten_values(valseq[1:])

def takeval(numvalues, values):
    if numvalues==1: return values.pop(0)
    else: return [values.pop(0) for i in range(numvalues)]

def struct_format(byte_order, elements):
    return byte_order+" ".join(elements)
   
def aggregate(formats, values):
    return [takeval(numvalues(format), values) for format in formats]

def struct_unpack(infile, byte_order, elements):
    format = struct_format(byte_order, elements)
    return aggregate(elements,
      list(unpack(format, infile.read(calcsize(format)))))

def struct_pack(byte_order, elements, values):
    format = struct_format(byte_order, elements)
    return pack(format, *flatten_values(values))

def touch(fname):
    open(fname, 'w')

#### To filter data written to a file
def cast_data(data, new_sctype, default_scale):
    "casts numbers in data to desired typecode in data_code"
    # if casting to an integer type, check the data range
    # if it clips, then scale down
    # if it has poor integral resolution, then scale up
    if new_sctype in integer_ranges.keys():
        maxval = abs(data.max())
        maxrange = integer_ranges[new_sctype]
        scl = maxval/maxrange or 1.
        return scl, N.round(data/scl)
    return default_scale, data

@@ -720,24 +720,25 @@
 ts = %5B%5D%0Afor 
+_
 formats in _
@@ -752,16 +752,20 @@
 .keys():
+%0A   
  allform
@@ -775,16 +775,17 @@
 .extend(
+_
 formats)
@@ -816,38 +816,96 @@
 : 0,
+ 
 'I':0,
- 'h': 0, 'H':0, 'f': 0.,
+%0A                   'h': 0, 'H':0,%0A                   'f': 0.,%0A                  
  'c'
@@ -911,17 +911,16 @@
 ': '%5C0',
-%5C
 %0A       
@@ -942,16 +942,55 @@
 :0.,
-'s': '',
+%0A                   's': '',%0A                  
  'B'
@@ -1093,24 +1093,79 @@
 es(format):%0A
+    %22%22%22 The number of values for the given format. %22%22%22%0A
     numstr, 
@@ -1388,16 +1388,28 @@
 formats:
+%0A           
  return 
@@ -1880,11 +1880,13 @@
     
-# f
+%22%22%22 F
 latt
@@ -1939,16 +1939,21 @@
 ggregate
+. %22%22%22
 %0A    if 
@@ -1977,16 +1977,24 @@
 ype(%5B%5D):
+%0A       
  return 
@@ -2022,16 +2022,24 @@
 q == %5B%5D:
+%0A       
  return 
@@ -2130,18 +2130,16 @@
 l(numval
-ue
 s, value
@@ -2150,24 +2150,144 @@
     
-if numvalues
+%22%22%22 Take numvals from values.%0A%0A    Returns a single value if numvals == 1 or else a list of values.%0A    %22%22%22%0A    if numvals 
 ==
+ 
 1:
+%0A       
  ret
@@ -2313,16 +2313,24 @@
    else:
+%0A       
  return 
@@ -2348,17 +2348,17 @@
 (0) for 
-i
+_
  in rang
@@ -2365,18 +2365,16 @@
 e(numval
-ue
 s)%5D%0A%0Adef
@@ -2909,15 +2909,59 @@
 ch(f
+ile
 name):%0A
+    %22%22%22 Ensure that filename exists. %22%22%22%0A
     
@@ -2966,16 +2966,19 @@
   open(f
+ile
 name, 'w

add docstrings and clean up