#!/usr/bin/env python
"""
$Id: hdf.py,v 1.13 2004/03/10 20:31:28 rayg Exp $

Simplified test for ResamplingStage executable.

Author: R.K.Garcia
"""


#from mmap import *
from struct import *
from math import sin

import os.path
import string
import os
import operator
import glob
from exceptions import Exception
import jarray
from java.lang import String
from java.lang import System, Runtime
from java.io import DataInputStream, IOException
from java.nio import *

class SDError( Exception ):
    def __init__( self, rc ):
        self.args = ( rc, )
        
def which( exe, *args ):
    """
    which( executable-name[, optional-pathlist[, second-pathlist...]] )
    If full path to the executable is specified, return the executable as the first member of a list.
    Optionally, further path strings can be included as preferred search locations.
    Otherwise search the system path for instances of the executable, and return a list of them in order.
    """
    if os.path.exists( exe ): return [ exe ]
    path = string.join( (list(args) or []) + [ os.environ[ 'PATH' ] ], os.pathsep )
    return reduce( operator.add, [ glob.glob( os.path.join( D, exe ) ) for D in path.split( os.pathsep ) ], [] )


def invoke( exe ):
    rt = Runtime.getRuntime()
    proc =  rt.exec(exe)
    writeable = proc.getOutputStream()
    readable = proc.getInputStream()
    return writeable, readable, proc

def readStruct( (writable0, readable1, pid ), format='i' ):
    'read a structure of known content from the pipe'
    b = jarray.zeros(calcsize(format), 'b')
    n = readable1.read(b)
    return unpack( format, String(b,0) )
    
def readBlock( ( writable0, readable1, pid ), kind = 'c' ):
    'read a length-first block of data from the plugin pipe'
    b = jarray.zeros(4, 'b')
    n = readable1.read(b,0,4)
    nelem = unpack( '<i', String(b,0) )[0]
    fmt = '%d%s' % ( nelem, kind )
    b = jarray.zeros(calcsize(fmt),'b')
    n = readable1.read(b)
    return unpack( fmt, String(b,0))

    
def readRawBlock( ( writable0, readable1, pid ), data_type=None, element_count=None ):
    'read a length-first block of byte data from the plugin pipe'
    b = jarray.zeros(4,'b')
    n = readable1.read(b)
    bb = ByteBuffer.allocate(4)
    bb.put(b)
    bb.order(ByteOrder.LITTLE_ENDIAN)
    bb.rewind()
    ib = bb.asIntBuffer()

    nbytes = ib.get()

    b_buf = ByteBuffer.allocate(nbytes)
    b_ary = jarray.zeros(nbytes, 'b')
    n = readable1.read(b_ary)
    b_buf.put(b_ary)
    b_buf.order(ByteOrder.LITTLE_ENDIAN)
    b_buf.rewind()

    if data_type == 'I' or data_type == 'i':
      int_buf = b_buf.asIntBuffer()
      int_ary = jarray.zeros(element_count, 'i')
      int_buf.get(int_ary)
      return int_ary
    if data_type == 'H' or data_type == 'h':
      srt_buf = b_buf.asShortBuffer()
      srt_ary = jarray.zeros(element_count, 'h')
      srt_buf.get(srt_ary)
      return srt_ary
    if data_type == 'd':
      db = b_buf.asDoubleBuffer()
      da = jarray.zeros(element_count, 'd')
      db.get(da)
      return da
    if data_type == 'f':
      flt_buf = b_buf.asFloatBuffer()
      flt_ary = jarray.zeros(element_count, 'f')
      flt_buf.get(flt_ary)
      return flt_ary

    b = jarray.zeros(nbytes, 'b')
    n = readable1.read(b)
    return b
    
    
def command( (writable0,readable1,pid), cmd, parmblock = '' ):
    writable0.write( pack( '<i', cmd ) + parmblock )
    writable0.flush()
    # read returned result code 
    b = jarray.zeros(4,'b')
    n = readable1.read(b)
    return unpack( '<i', String(b,0) )[0]

def stringBlock( str ):
    return pack( '<i', len(str) ) + str
    
def intBlock( L ):
    return pack( '<%di' % (1+len(L)), len(L), *L )
        
# command implementations per README

# module's connection to the plug-in executable
#plug_hdf = which( 'plug_hdf' )[0] 

sys_name = System.getProperty('os.name')

if sys_name.find('Linux') >= 0 or sys_name.find('linux') >= 0:
  plug_hdf = './plug_hdf'
elif sys_name.find('Windows') >= 0 or sys_name.find('windows') >= 0:
  plug_hdf = '.\plug_hdf.exe'
elif sys_name.find('Mac') >= 0:
  plug_hdf = './plug_hdf'
else:
  plug_hdf = './plug_hdf'

LINK = invoke( plug_hdf )    

def ping( ):
    return command( LINK, 0 )
    
def ident():
    rc = command( LINK, 1 )
    if rc>=0:
        str = readBlock( LINK, 's' )
        return str[0]
    return rc

def shutdown( LINK ):
    return command( LINK, -1 )


def start( filename ):
    rc = command( LINK, 2, stringBlock( filename ) )
    if rc>=0: return readStruct( LINK, '<i' )[0]
    raise SDError( rc ) # 'SDstart failed'
        
def select( sd_id, index ):
    rc = command( LINK, 3, pack( '<ii', sd_id, index ) )
    if rc>=0: return readStruct( LINK, '<i' )[0]
    raise SDError( rc ) # 'SDselect failed'

def readattr( id, index ):
    rc = command( LINK, 4, pack( '<ii', id, index ) )
    if rc>=0:
        data_type = readBlock( LINK, 's' )[0]
        elementCount = readStruct( LINK, '<i' )[0]
        return data_type, elementCount, readRawBlock( LINK, data_type, elementCount )
    raise SDError( rc ) # 'SDreadattr failed'

def findattr( id, name ):
    rc = command( LINK, 5, pack( '<i', id ) + stringBlock( name ) )
    if rc>=0: return readStruct( LINK, '<i' )[0]
    raise SDError( rc ) # 'SDfindattr failed'

def readdata( sds_id, start, stride, edges ):
    blox = intBlock( start ) + intBlock( stride ) + intBlock( edges )
    rc = command( LINK, 6, pack( '<i', sds_id ) + blox ) 
    if rc>=0:
        data_type = readBlock( LINK, 's' )[0]
        elementCount = readStruct( LINK, '<i' )[0]
        return data_type, elementCount, readRawBlock( LINK, data_type, elementCount )
    raise SDError( rc ) # 'SDreaddata failed'

        
def endaccess( sds_id ):
    return command( LINK, 7, pack( '<i', sds_id ) )
    
def nametoindex( sd_id, sds_name):
    rc = command( LINK, 8, pack( '<i', sd_id ) + stringBlock( sds_name) )
    if rc>=0: return readStruct( LINK, '<i' )[0]
    raise SDError( rc ) # 'SDnametoindex failed'

def end( sd_id ):
    return command( LINK, 9, sd_id )

def getdimid( sds_id, dim_index ):
    rc = command( LINK, 10, pack( '<ii', sds_id, dim_index) )
    if rc>=0:
        return readStruct( LINK, '<i' )[0]
    raise SDError( rc ) # 'SDgetdimid failed'

def diminfo( dim_id ):
    rc = command( LINK, 11, pack( '<i', dim_id ))
    if rc>=0:
        dim_name = readBlock( LINK, 's' )[0]
        dim_size = readStruct( LINK, '<i' )[0]
        dim_type_code = readStruct( LINK, '<i' )[0]
        dim_n_attrs   = readStruct( LINK, '<i' )[0]
        return dim_name, dim_size, dim_type_code, dim_n_attrs
    raise SDError( rc ) # 'SDdiminfo failed'

def getinfo( sds_id ):
    rc = command( LINK, 13, pack( '<i', sds_id ))
    if rc>=0:
       var_name = readBlock( LINK, 's' )[0]
       var_rank = readStruct( LINK, '<i' )[0]
       var_data_type = readStruct( LINK, '<i' )[0]
       var_num_attrs = readStruct( LINK, '<i' )[0]
       return var_name, var_rank, var_data_type, var_num_attrs

def fileinfo( sd_id ):
    rc = command( LINK, 12, pack( '<i', sd_id ))
    if rc>=0:
        num_datasets = readStruct( LINK, '<i' )[0]
        num_global_attrs = readStruct( LINK, '<i' )[0]
        return num_datasets, num_global_attrs
    raise SDError( rc ) # 'SDfileinfo failed'

class HDFvariable: 
    def __init__( self, parent, sd_id, sds_id ):
        self.sd_id  = sd_id
        self.sds_id = sds_id
        self.parent = parent

    def readattr( self, index ):
        return readattr( self.sds_id, index )
        
    def findattr( self, name ):
        return findattr( self.sds_id, name )
        
    def readdata( self, start, stride, edges ):
        return readdata( self.sds_id, start, stride, edges )

    def getdimid( self, dim_index ):
        return getdimid(self.sds_id, dim_index)

    def getinfo( self ):
        return getinfo(self.sds_id)
        
    def id(self):
        return self.sds_id

    def __repr__(self):
        return "HDF variable object %d (child of %s)" % (self.sds_id, `self.parent`)
        
    def __del__(self):
        endaccess( self.sds_id )

        
    
class HDFfile:

    def __init__( self, filename ):
        self.sd_id = start( filename )

    def select( self, index ):
        return HDFvariable( self, self.sd_id, select( self.sd_id, index ) )
        
    def readattr( self, index ):
        return readattr( self.sd_id, index )
        
    def findattr( self, name ):
        return findattr( self.sd_id, name )

    def nametoindex( self, sds_name ):
        return nametoindex( self.sd_id, sds_name )

    def diminfo( self, dim_id ):
        return diminfo( dim_id )

    def fileinfo( self ):
        return fileinfo( self.sd_id )
        
    def id( self ): 
        return self.sd_id
        
    def __repr__(self):
        return "HDF file object %d" % self.sd_id

    def __del__(self):
        end( self.sd_id )


HDF = HDFfile


rc = ping()
if 0!=rc:
    raise SDError( rc ) # 'ping failure'
del rc
    
print ident()

def test1():
    print "forking executable stage.." 
    wrp = invoke( './plug_hdf' )
    print "ping result: %s" % ping( wrp )
    print "ident result: %s" % ident( wrp )
    #print "resample result: %s" % resample( wrp )
    print "exit request result: %s" % doexit( wrp )
    print "waiting for exit.." 
    os.waitpid( wrp[2], 0 )
    print "done."

def test2( args ):
    f = HDFfile( args[0] )
    file = f
    file_info        = file.fileinfo()
    num_datasets     = file_info[0]
    num_global_attrs = file_info[1]


    #- set-up dictionarys for variables/dimensions
    vars = {}
    dims = {}
    vv   = []

    for i in xrange(num_datasets):  # loop through all variables
      vv.append(file.select(i))

    for i in xrange(num_datasets):  # loop through all variables
      var = vv[i]
      info =  var.getinfo()
      dim_names = []
      dim_lengths = []
      for j in xrange(info[1]):
        dim_id = var.getdimid(j)
        dim_info = file.diminfo(dim_id)
        dims[dim_info[0]] = dim_info[1]
        dim_names.append(dim_info[0])
        dim_lengths.append(dim_info[1])
      vars[info[0]] = [var, dim_names, dim_lengths]


    print vars
    print '-------------------------'
    print dims

    print f
    print f.fileinfo()
    print "-----"
    #print f.nametoindex("EV_1KM_RefSB")
    v = f.select(0)
    #print v.getinfo()
    #d_id =  v.getdimid(0)
    #print f.diminfo(d_id)

    #v = f.select(22)
    print v, vv[0]
    #print String((f.readattr(0))[2])
    #print v.readattr(0)
    a_i = v.findattr("radiance_scales")
    print v.readattr(a_i)

    #-- read multi array (everything)
    var = vars["EV_1KM_RefSB"][0]
    start  = []
    stride = []
    size   = []
    dim_lengths = vars["EV_1KM_RefSB"][2]
    rank = len(dim_lengths)
    for i in xrange(rank):
      start.append(0)
      stride.append(1)
      size.append(dim_lengths[i])
    print start, stride, size

    try:
      rr =  v.readdata([0,0,0],[1,1,1],[1,5000,1354])
      print rr[2][500:550]
      rr = v.readdata([0,0,0], [1,1,1], [1,5000,1354])
      print rr[2][500:550]
    except:
      print 'Exception'


if __name__=='__main__':
    from sys import argv
    test2( argv[1:] )
