#!/usr/bin/env python
#*-----------------------------------------------------------------------*
#|                                                                       |
#|  Copyright (c) 2013 by Paul Scherrer Institute (http://www.psi.ch)    |
#|                                                                       |
#|              Author Thierry Zamofing (thierry.zamofing@psi.ch)        |
#*-----------------------------------------------------------------------*
'''
hdf5 viewer to dispay images, tables, attributes and trees of a hdf5 file.
'''
import os,sys
import wx,h5py
import wx.py
from hdfTree import *
from hdfGrid import *
from hdfAttrib import *
from hdfImage import  *
from hdfImageGL import  *
import utilities as ut


class AboutFrame(wx.Frame):
  def __init__(self,parent):
    wx.Frame.__init__(self,parent,-1,'About h5pyViewer',size=(300,330))
    imgDir=ut.Path.GetImage()
    icon = wx.Icon(os.path.join(imgDir,'h5pyViewer.ico'), wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    self.Centre()
    panel=wx.Panel(self,-1)
    import pkg_resources

    v=pkg_resources.get_distribution("h5pyViewer")
    s='Version:'+str(v)+'\n(c) www.psi.ch\n Author: Thierry Zamofing\n thierry.zamofing@psi.ch'

    st0=wx.StaticText(panel,-1,s,(30,10))
    bmp = wx.StaticBitmap(panel,-1,wx.Bitmap(os.path.join(imgDir,'splash1.png'), wx.BITMAP_TYPE_ANY ), (30,st0.Position[1]+st0.Size[1]+10))

class HdfTreePopupMenu(wx.Menu):
  def __init__(self, wxObjSrc):
    wx.Menu.__init__(self)
    self.wxObjSrc=wxObjSrc
    self.AddMenu(self.OnShowAttrib,"Show Attributes")
    self.AddMenu(self.OnShowData,"Show Data")
    self.AddMenu(self.OnShowImage,"Show Image")
    self.AddMenu(self.OnShowImageGL,"Show Image OpenGL")
    self.AddMenu(self.OnShell,"Python Shell")
    self.AddMenu(self.OnPrintProperties,"Print Properties")
    self.AddMenu(self.OnItem2,"Item Two")
    self.AddMenu(self.OnItem3,"Item Three")

  def AddMenu(self,func,lbl):
    item = wx.MenuItem(self, -1, lbl)
    self.AppendItem(item);
    self.Bind(wx.EVT_MENU, func, item)
    return item
    
  def OnShowAttrib(self, event):
    wxTree,wxNode=self.wxObjSrc
    lbl=wxTree.GetItemText(wxNode)
    hid=wxTree.GetPyData(wxNode)
    frame=HdfAttribFrame(wxTree,lbl,hid)
    frame.Show(True)
    
  def OnShowData(self, event):
    wxTree,wxNode=self.wxObjSrc
    lbl=wxTree.GetItemText(wxNode)
    hid=wxTree.GetPyData(wxNode)
    frame=HdfGridFrame(wxTree,lbl,hid)
    frame.Show(True)  

  def OnShowImage(self, event):
    wxTree,wxNode=self.wxObjSrc
    lbl=wxTree.GetItemText(wxNode)
    hid=wxTree.GetPyData(wxNode)
    frame=HdfImageFrame(wxTree,lbl,hid)
    frame.Show(True)  

  def OnShowImageGL(self, event):
    wxTree,wxNode=self.wxObjSrc
    lbl=wxTree.GetItemText(wxNode)
    hid=wxTree.GetPyData(wxNode)
    frame=HdfImageGLFrame(wxTree,lbl,hid)
    frame.Show(True)     
    
  def OnShell(self, event):
    wxTree,wxNode=self.wxObjSrc
    frame = wx.Frame(wxTree, -1, "wxPyShell",size=wx.Size(800, 500))
    imgDir=ut.Path.GetImage()
    icon = wx.Icon(os.path.join(imgDir,'h5pyViewer.ico'), wx.BITMAP_TYPE_ICO)
    frame.SetIcon(icon)
    frame.Centre()
    wnd=app.GetTopWindow() 
    loc={'app'  :app,
         'fid'  :app.GetTopWindow().fid,
         'lbl'  :wxTree.GetItemText(wxNode),
         'hid'  :wxTree.GetPyData(wxNode),
         'h5py' : h5py
         }
    introText='''Shell to the HDF5 objects
app: application object
fid: hdf5 file object
lbl: label of selected hdf5 object
hid: selected hdf5 object

#Examples:
import h5py
ds=h5py.Dataset(hid)
ds[1,:,:]

#using user defined modules
import userSample as us;reload(us);us.test1(hid)

'''
    shell=wx.py.shell.Shell(frame, introText=introText,locals=loc)
    frame.Show(True)
    #if loc is None, all variables are visible. the context is global
    #shell.push('wnd=app.GetTopWindow()')
    #for cmd in [
    #  'wnd=app.GetTopWindow();wxTree=wnd.wxTree',
    #  'wxNode=wnd.wxTree.GetSelection()',
    #  'print wnd.fid',
    #  'lbl=wxTree.GetItemText(wxNode)',
    #  'hid=wxTree.GetPyData(wxNode)']:
    #  shell.run(cmd, prompt=False)

  def OnPrintProperties(self, event):
    wxTree,wxNode=self.wxObjSrc
    lbl=wxTree.GetItemText(wxNode)
    hid=wxTree.GetPyData(wxNode)
    print HdfViewerFrame.GetPropertyStr(wxTree,wxNode)
    
  def OnItem2(self, event):
    print 'OnItem2'
    GlumpyViewHdf5()
    pass

  def OnItem3(self, event):
    print 'OnItem3'
    frame = GLFrame(None, -1, 'GL Window')
    frame.Show()
    pass

class HdfViewerFrame(wx.Frame):

  def OpenFile(self,fnHDF):
    try:
      self.fid=h5py.h5f.open(fnHDF)
    except IOError as e:
      sys.stderr.write('Unable to open File: '+fnHDF+'\n')
    else: 
      self.wxTree.ShowHirarchy(self.fid)

  def CloseFile(self):
    #http://docs.wxwidgets.org/2.8/wx_windowdeletionoverview.html#windowdeletionoverview
    print 'CloseFile'
    try:
      self.fid.close()
      del self.fid
    except AttributeError as e:
      pass

  def __init__(self, parent, title):
    wx.Frame.__init__(self, parent, title=title, size=wx.Size(650, 350))
    imgDir=ut.Path.GetImage()
    icon = wx.Icon(os.path.join(imgDir,'h5pyViewer.ico'), wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    wxSplt = wx.SplitterWindow(self, -1)
    wxTree = HdfTreeCtrl(wxSplt, 1, wx.DefaultPosition, (-1,-1),  wx.TR_HAS_BUTTONS)
    wxTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1)
    wxTree.Bind(wx.EVT_TREE_ITEM_MENU, self.OnMenu, id=1)
    #wx.EVT_TREE_ITEM_MENU(id, func)
    wxTxt = wx.StaticText(wxSplt, -1, '',(10,10) )#, style=wx.ALIGN_CENTRE)

    wxSplt.SplitVertically(wxTree, wxTxt)

    #wxLstCtrl=HdfAttrListCtrl(wxSplt)
    #wxSplt.SplitVertically(wxTree, wxLstCtrl)
    self.BuildMenu()
    
    self.Centre()

    self.wxTree=wxTree
    self.display=wxTxt
  def __del__(self):
    self.CloseFile()
  
  def OnOpen(self, event):
    dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), '','HDF5 files (*.hdf5)|*.hdf5|all (*.*)|*.*', wx.OPEN|wx.FD_CHANGE_DIR)
    if dlg.ShowModal() == wx.ID_OK:
      path = dlg.GetPath()
      #mypath = os.path.basename(path)
      #self.SetStatusText("You selected: %s" % mypath)
      self.CloseFile()
      self.OpenFile(path)
      print 'OnOpen',path
    dlg.Destroy()       
  
  def OnCloseWindow(self, event):
    print 'OnCloseWindow'
    self.Destroy()
    
  def OnAbout(self,event):
    frame=AboutFrame(self)
    frame.Show()
    
  def BuildMenu(self):
    #http://wiki.wxpython.org/AnotherTutorial#wx.MenuBar
    mnBar = wx.MenuBar()

    #-------- File Menu --------
    mn = wx.Menu()
    mnItem=mn.Append(wx.ID_OPEN, '&Open', 'Open a new document');self.Bind(wx.EVT_MENU, self.OnOpen, mnItem)
    #mnSub = wx.Menu()
    #mnItem=mnSub.Append(wx.ID_ANY, 'SubMenuEntry', 'My SubMenuEntry')
    #mn.AppendMenu(wx.ID_ANY, 'SubMenu', mnSub)   
    mn.AppendSeparator()
    mnItem=mn.Append(wx.ID_EXIT, '&Quit', 'Quit the Application');self.Bind(wx.EVT_MENU, self.OnCloseWindow, mnItem)
    mnBar.Append(mn, '&File')
    
    self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

    #-------- Edit Menu --------
    #mn = wx.Menu()
    #mnBar.Append(mn, '&Edit')

    #-------- Help Menu --------
    mn = wx.Menu()
    #mnItem=mn.Append(wx.ID_HELP,'Help','Application Help')
    mnItem=mn.Append(wx.ID_ABOUT,'About','Application About');self.Bind(wx.EVT_MENU, self.OnAbout, mnItem)
    mnBar.Append(mn, '&Help')
          
    #mn.AppendSeparator()
    #mnItem = wx.MenuItem(mn, 105, '&Quit\tCtrl+Q', 'Quit the Application')
    #mnItem.SetBitmap(wx.Image('stock_exit-16.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap())
    #mn.AppendItem(mnItem)   
    self.SetMenuBar(mnBar)
    self.CreateStatusBar()   

  @staticmethod
  def GetPath(wxTree,wxNode):
    if wxTree.GetRootItem()==wxNode:
      hid=wxTree.GetPyData(wxNode)
      return hid.name
    wxNodeParent=wxTree.GetItemParent(wxNode)
    if wxTree.GetRootItem()==wxNodeParent:
      return wxTree.GetItemText(wxNode)
    else:
      return HdfViewerFrame.GetPath(wxTree,wxNodeParent)+'/'+wxTree.GetItemText(wxNode)

  @staticmethod
  def GetPropertyStr(wxTree,wxNode):
    
    path=HdfViewerFrame.GetPath(wxTree,wxNode)
    
    hidStr=wxTree.GetItemText(wxNode)
    hid=wxTree.GetPyData(wxNode)
    #o=wxTree.GetItemData(wxNode)
    #print o.Data,wxTree.GetPyData(wxNode)
    #if type(gid)==h5py.h5g.GroupID:
    txt=path+'\n'
    objInf=h5py.h5o.get_info(hid)
    t=type(hid)
    #print t,hid.id,objInf.fileno, objInf.rc, objInf.type, objInf.addr, objInf.hdr       
    txt+=type(hid).__name__+':%d\n'%hid.id     
    txt+='addr:%d fileno:%d refCnt:%d\n'%(objInf.addr,objInf.fileno, objInf.rc)      
    try:
      wxNodeParent=wxTree.GetItemParent(wxNode)
      txtParent=wxTree.GetItemText(wxNode)
      dataParent=wxTree.GetPyData(wxNode)
      gid=wxTree.GetPyData(wxNodeParent)
      softLnk=gid.get_linkval(hidStr)
    except BaseException as e:
      pass
    else:
      txt+='Soft Link:'+softLnk+'\n'
    try: numAttr=h5py.h5a.get_num_attrs(hid)
    except ValueError as e:
      pass
    else:
      txt+='Attributes:%d\n'%numAttr
    if t==h5py.h5g.GroupID:
      pass
    elif t==h5py.h5d.DatasetID:
      txt+='shape: '+str(hid.shape)+'\n'
      tt=hid.get_type()
      ttt=type(tt)
      if ttt==h5py.h5t.TypeCompoundID:
        txt+='type: Compound\n'
      elif ttt==h5py.h5t.TypeStringID:
        sz=tt.get_size()
        txt+='type: String (length %d)\n'%sz
      else:
        txt+='type: '+str(tt.dtype)+'\n'

    
      pl=hid.get_create_plist()
      txFcn=(
       ('chunk',h5py.h5p.PropDCID.get_chunk),
       ('fill time',   h5py.h5p.PropDCID.get_fill_time),
       ('alloc_time',  h5py.h5p.PropDCID.get_alloc_time),
       #('class',       h5py.h5p.PropDCID.get_class),
       #('fill_value',  h5py.h5p.PropDCID.get_fill_value),
       #('filter',      h5py.h5p.PropDCID.get_filter),
       #('filter_by_id',h5py.h5p.PropDCID.get_filter_by_id),
       ('layout',      h5py.h5p.PropDCID.get_layout),
       ('nfilters',    h5py.h5p.PropDCID.get_nfilters),
       #('obj_track_times', h5py.h5p.PropDCID.get_obj_track_times),
       )
      for tx,func in txFcn:
        try: v=func(pl)
        except ValueError as e: pass
        else:txt+=tx+':'+str(v)+'\n'
      if hid.shape==(1,):
        data=h5py.Dataset(hid)
        txt+='value:'+str(data[0])+'\n'
    return txt
    
  def OnSelChanged(self, event):
      wxNode =  event.GetItem()
      txt=HdfViewerFrame.GetPropertyStr(self.wxTree,wxNode)            
      self.display.SetLabel(txt)

  def OnMenu(self, event):
      wxNode =  event.GetItem()
      self.PopupMenu(HdfTreePopupMenu((self.wxTree,wxNode)), event.GetPoint())    

if __name__ == '__main__':
  def GetArgs():   
    import sys,argparse #since python 2.7
    fnHDF='/scratch/detectorData/e14472_00033.hdf5'

    exampleCmd='--hdfFile='+fnHDF    
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
                                     description=__doc__,
                                     epilog='Example:\n'+os.path.basename(sys.argv[0])+' '+exampleCmd+'\n ')
    parser.add_argument('--hdfFile', default=fnHDF, help='the hdf5 to show')
    
    args = parser.parse_args()
    return args

  class MyApp(wx.App):
    def OnInit(self):
      args=GetArgs()
      frame = HdfViewerFrame(None, 'h5pyViewer')
      frame.OpenFile(args.hdfFile)
      frame.Show(True)
      self.SetTopWindow(frame)
      return True
    
#------------------ Main Code ----------------------------------    
  app = MyApp()
  app.MainLoop()
