import numpy
import types

def _find_dim(grp, dimname):
    # find Dimension instance given group and name.
    # look in current group, and parents.
    group = grp
    dim = None
    while 1:
        try:
            dim = group.dimensions[dimname]
            break
        except:
            try:
                group = group.parent
            except:
                raise ValueError("cannot find dimension %s in this group or parent groups" % dimname)
    return dim

def _buildStartCountStride(elem, shape, dimensions, grp):

    # Create the 'start', 'count', and 'stride' tuples that
    # will be passed to 'nc_get_var'/'nc_put_var'.
    #   start     starting indices along each dimension
    #   count     count of values along each dimension; a value of -1
    #             indicates that and index, not a slice, was applied to
    #             the dimension; in that case, the dimension should be
    #             dropped from the output array.
    #   stride    strides along each dimension

    # Adapted from pycdf (http://pysclint.sourceforge.net/pycdf)
    # by Andre Gosselin.

    # Handle a scalar variable as a 1-dimensional array of length 1.

    nDims = len(dimensions)
    if nDims == 0:
        nDims = 1
        shape = (1,)

    # Make sure the indexing expression does not exceed the variable
    # number of dimensions.
    if type(elem) == types.TupleType:
        if len(elem) > nDims:
            raise ValueError("slicing expression exceeds the number of dimensions of the variable")
    else:   # Convert single index to sequence
        elem = [elem]
        
    # 'elem' is a tuple whose element types can be one of:
    #    IntType      for standard indexing
    #    SliceType    for extended slicing (using 'start', 'stop' and 'step' attributes)
    #    EllipsisType for an ellipsis (...); at most one ellipsis can occur in the
    #                 slicing expression, otherwise the expressionis ambiguous
    # Recreate the 'elem' tuple, replacing a possible ellipsis with empty slices.
    hasEllipsis = 0
    newElem = []
    for e in elem:
        if type(e) == types.EllipsisType:
            if hasEllipsis:
                raise IndexError("at most one ellipsis allowed in a slicing expression")
            # The ellipsis stands for the missing dimensions.
            newElem.extend((slice(None, None, None),) * (nDims - len(elem) + 1))
        else:
            newElem.append(e)
    elem = newElem

    # Build start, count, stride tuples.
    start = []
    count = []
    stride = []
    sliceout = []
    hasfancyindex = False
    n = -1
    for e in elem:
        n = n+1

        if len(dimensions):
            dimname = dimensions[n]
            # is this dimension unlimited?
            # look in current group, and parents for dim.
            dim = _find_dim(grp, dimname)
            unlim = dim.isunlimited()
        else:
            unlim = False

        try:
            e[:]
            isSequenceType = True
        except:
            isSequenceType = False
        if isSequenceType and type(e) == types.StringType:
            isSequenceType = False
        
        # Slice index. Respect Python syntax for slice upper bounds,
        # which are not included in the resulting slice. Also, if the
        # upper bound exceed the dimension size, truncate it.
        if type(e) == types.SliceType:
            isSlice = 1     # we deal with a slice
            # None means not specified
            if e.step is not None:
                inc = e.step
            else:
                inc = 1
            if shape[n]: 
                if unlim and e.stop > shape[n]:
                    beg, end, inc = e.indices(e.stop)
                else:
                    beg, end, inc = e.indices(shape[n])
            else:
                if inc > 0:
                    if e.stop is None:
                        raise IndexError('illegal slice')
                    else:
                        length = e.stop
                else:
                    if e.start is None:
                        raise IndexError('illegal slice')
                    else:
                        length = e.start+1
                beg, end, inc = e.indices(length)
            sliceout.append(slice(None,None,None))
        elif isSequenceType: # a sequence for 'fancy indexing'
        # just grab all the data along this dimension
        # then slice the resulting numpy array with the sequence
            isSlice = 1
            hasfancyindex = 1
            beg, end, inc = 0, shape[n], 1
            sliceout.append(e)
        # assume it's a simple index
        else: 
            # if it's not an integer, try to convert it to one.
            if type(e) != types.IntType:
                e = int(e)
            isSlice = 0       # not a slice
            # Respect standard python sequence indexing behavior.
            # Count from the dimension end if index is negative.
            # Consider as illegal an out of bound index, except for the
            # unlimited dimension.
            if e < 0 :
                e = e+shape[n]
            if e < 0 or (not unlim and e >= shape[n]):
                raise IndexError("index out of range")
            beg = e
            end = e + 1
            inc = 1

        # Clip end index (except if unlimited dimension)
        # and compute number of elements to get.
        if not unlim and end > shape[n]:
            end = shape[n]
        if isSlice:   
            cnt = len(xrange(beg,end,inc))
        else: 
            cnt = -1 # -1 means a single element.
        start.append(beg)
        count.append(cnt)
        stride.append(inc)

    # Complete missing dimensions
    while n < nDims - 1:
        n = n+1
        start.append(0)
        count.append(shape[n])
        stride.append(1)
        sliceout.append(slice(None,None,None))

    # if no fancy indexing requested, just set sliceout to None.
    if not hasfancyindex: sliceout = None

    # Done
    return start, count, stride, sliceout

def _quantize(data,least_significant_digit):
    """

quantize data to improve compression. data is quantized using 
around(scale*data)/scale, where scale is 2**bits, and bits is determined 
from the least_significant_digit. For example, if 
least_significant_digit=1, bits will be 4.

This function is pure python.

    """
    precision = pow(10.,-least_significant_digit)
    exp = numpy.log10(precision)
    if exp < 0:
        exp = int(numpy.floor(exp))
    else:
        exp = int(numpy.ceil(exp))
    bits = numpy.ceil(numpy.log2(pow(10.,-exp)))
    scale = pow(2.,bits)
    return numpy.around(scale*data)/scale

def _parsedtype(d,parent=None):
    """
returns a nested dictionary containing information about a numpy
datatype.  The dict values are tuples with (dtype,shape,offset)."""
    if d.fields is None:
        return d.str[1:]
    o = {}
    for f,v in d.fields.iteritems():
        shape = v[0].shape
        if not shape:
            shape = (1,)
        offset = v[1]
        if v[0].type == numpy.void and v[0].subdtype is None:
            o[f] = _parsedtype(v[0],parent=f),shape,offset,parent
        else:
            if v[0].subdtype is not None:
                o[f] = v[0].subdtype[0].str[1:],shape,offset,parent
            else:
                o[f] = v[0].str[1:],shape,offset,parent
    return o

@@ -2931,30 +2931,148 @@
     
-try:%0A            e%5B:%5D%0A
+# is this element of the tuple a sequence? (but not a string)%0A        try:%0A            e%5B:%5D%0A            if type(e) != types.StringType:%0A    
     
@@ -3101,16 +3101,20 @@
  = True%0A
+    
         
@@ -3106,39 +3106,41 @@
 ue%0A            e
-xcept:%0A
+lse:%0A    
             isSe
@@ -3170,57 +3170,14 @@
     
-if isSequenceType and type(e) == types.StringType
+except
 :%0A  

check that slice element not a string