#!/usr/bin/python
#for MacOSX, maybe calling python2.6 instead of python may be required

import os, sys, ROOT, numpy as np, array, math

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)

widgetsIds = enum("mBar", "mOpen", "bb100p", "bb10p", "bb10GTU", "tGTU", "bf10GTU", "bf10p", "bf100p", "bAnimate", "tAnimationSpeed")
treeWidgetsIds = enum("mDerivative", "mSecondDerivative", "mFindPeak", "mPeriodogram", "mPixelsStatistics", "mSPEStatistics", "mAverage")

# # Main window containing pixel (PDM/PMT) view
class pMainFrame(ROOT.TGMainFrame):
	def handle_navigation(self):
		return 0
	
	# # Constructor
	def __init__(self, parent, width, height, etosTree):
		
		self.width = width
		self.height = height
		
		self.nav_dispatch = ROOT.TPyDispatcher(self.handle_navigation)
		
		ROOT.TGMainFrame.__init__(self, parent, width, height)
		self.parent = parent
		self.AnalysisFrame = None

		# Main frame
		self.MainFrame = ROOT.TGVerticalFrame(self, width, height)
		self.etosTree = etosTree

		# Menu bar
		self.MenuBar = ROOT.TGMenuBar(self, 1, 1, ROOT.kHorizontalFrame)
		self.MenuFile = ROOT.TGPopupMenu();
		self.MenuFile.AddEntry("&Open...", widgetsIds.mOpen)
		
		self.MenuBarItemLayout = ROOT.TGLayoutHints(ROOT.kLHintsTop | ROOT.kLHintsLeft, 0, 4, 0, 0)
		self.MenuBar.AddPopup("&File", self.MenuFile, self.MenuBarItemLayout);
		
		# Canvas 
		# This import main is supposedly for ipython compatibility
		import __main__
		__main__.slicer = self.handle_mouse_canvas
		self.Canvas	 = ROOT.TRootEmbeddedCanvas('Canvas', self.MainFrame, 200, 500)
		self.Canvas.GetCanvas().AddExec('dynamic', 'TPython::Exec( "slicer()" );')

		# Navigation bar
		self.NavigationBar = ROOT.TGHorizontalFrame(self.MainFrame, width, 40)
		self.bb100p = ROOT.TGTextButton(self.NavigationBar, '-100 packets', widgetsIds.bb100p)
		self.bb10p = ROOT.TGTextButton(self.NavigationBar, '-10 packets', widgetsIds.bb10p)
		self.bb10GTU = ROOT.TGTextButton(self.NavigationBar, '-10 GTU', widgetsIds.bb10GTU)
		self.tGTU = ROOT.TGNumberEntry(self.NavigationBar, 0, 7, widgetsIds.tGTU, ROOT.TGNumberFormat.kNESInteger, ROOT.TGNumberFormat.kNEANonNegative, ROOT.TGNumberFormat.kNELLimitMin)
		self.bf10GTU = ROOT.TGTextButton(self.NavigationBar, '+10 GTU', widgetsIds.bf10GTU)
		self.bf10p = ROOT.TGTextButton(self.NavigationBar, '+10 packets', widgetsIds.bf10p)
		self.bf100p = ROOT.TGTextButton(self.NavigationBar, '+100 packets', widgetsIds.bf100p)
		self.bAnimate = ROOT.TGTextButton(self.NavigationBar, 'Animate', widgetsIds.bAnimate)
		self.tAnimationSpeed = ROOT.TGNumberEntry(self.NavigationBar, 1, 3, widgetsIds.tAnimationSpeed, ROOT.TGNumberFormat.kNESInteger, ROOT.TGNumberFormat.kNEAPositive, ROOT.TGNumberFormat.kNELLimitMin)

		self.NavigationBar.AddFrame(self.bb100p, ROOT.TGLayoutHints())
		self.NavigationBar.AddFrame(self.bb10p, ROOT.TGLayoutHints())
		self.NavigationBar.AddFrame(self.bb10GTU, ROOT.TGLayoutHints())
		self.NavigationBar.AddFrame(self.tGTU, ROOT.TGLayoutHints())
		self.NavigationBar.AddFrame(self.bf10GTU, ROOT.TGLayoutHints())
		self.NavigationBar.AddFrame(self.bf10p, ROOT.TGLayoutHints())
		self.NavigationBar.AddFrame(self.bf100p, ROOT.TGLayoutHints())
		self.NavigationBar.AddFrame(self.bAnimate, ROOT.TGLayoutHints())
		self.NavigationBar.AddFrame(self.tAnimationSpeed, ROOT.TGLayoutHints())

		# Add frames to the main frame
		self.MainFrame.AddFrame(self.MenuBar)  # , ROOT.TGLayoutHints())
		# self.MainFrame.AddFrame(self.Toolbar, ROOT.TGLayoutHints(ROOT.kLHintsTop | ROOT.kLHintsExpandX))
		self.MainFrame.AddFrame(self.Canvas, ROOT.TGLayoutHints(ROOT.kLHintsExpandX | ROOT.kLHintsExpandY))
		self.MainFrame.AddFrame(self.NavigationBar, ROOT.TGLayoutHints(ROOT.kLHintsExpandX))
		self.AddFrame(self.MainFrame, ROOT.TGLayoutHints(ROOT.kLHintsExpandX | ROOT.kLHintsExpandY))

		self.SetWindowName('Pixels display')
		self.MapSubwindows()
		self.Resize(self.GetDefaultSize())
		self.MapWindow()
		
	# # Destructor
	def __del__(self):
		self.Cleanup()
		
	# # Draw the requested frame (GTU/threshold) from the analyzed TTree
	def DrawFrame(self, frame_num):
		self.etosTree.DrawFrame(frame_num)
		
	# # Handle mouse over canvas events
	def handle_mouse_canvas(self):
		hist = ROOT.gPad.GetSelected()
		# If mouse not over histogram reset the status bar
		if (not hist) or (not isinstance(hist, ROOT.TH2)):
			return 0
		# If mouse over histogram update the values in the status bar
		else:
			canv = self.Canvas.GetCanvas()
			cx, cy = canv.GetEventX(), canv.GetEventY()
			# Get the histogram bin
			abs_x = int(canv.AbsPixeltoX(cx))
			abs_y = int(canv.AbsPixeltoY(cy))

			print cx, cy, abs_x, abs_y
					
			# If histogram clicked (mouse-up) call pixel_click
			if ROOT.gPad.GetEvent() == 11:
				self.pixel_click(1)

		return 0
		
	def pixel_click(self, pix_num):
		if not self.AnalysisFrame: 
			self.AnalysisFrame = pAnalysisFrame(self.parent, 600, 250, self.etosTree)
		else: self.AnalysisFrame.Canvas.GetCanvas().cd()
		
		# Empty the stored curve lists
		self.etosTree.CurveY = []
		self.etosTree.SCurveGraph = []
		self.etosTree.Derivative = []
		self.etosTree.SecondDerivative = []
		self.etosTree.DerivativeGraph = []
		self.etosTree.SecondDerivativeGraph = []
		
		data = np.random.random(205)
		graph = ROOT.TGraph(len(data), data, data)
		graph.Draw("A*")

		self.etosTree.CurveY.append(data)
		self.etosTree.SCurveGraph.append(graph)
		self.etosTree.SCurveGraph[-1].SetName("SCurveGraph")
		#ROOT.gPad.GetPrimitive("htemp").GetYaxis().SetRangeUser(0,0.3) 
		ROOT.gPad.Modified()
		ROOT.gPad.Update()

		
		# Store the data used for ploting the graph
		# Need to manually specify length of the buffer due to numpy/PyROOT incompatibility
		self.etosTree.CurveX = data
	
		ROOT.gPad.Modified()
		ROOT.gPad.Update()
		
		print "Resizing"
		self.Canvas.GetCanvas().SetWindowSize(self.width + (self.width - self.Canvas.GetCanvas().GetWw()), self.height + (self.height - self.Canvas.GetCanvas().GetWh()))		


# # Main window containing pixel (PDM/PMT) view
class pAnalysisFrame(ROOT.TGMainFrame):
	# # Constructor
	def __init__(self, parent, width, height, etosTree):
		ROOT.TGMainFrame.__init__(self, parent, width, height)
		
		self.SecondCanvas = None
		self.ThirdCanvas = None

		self.etosTree = etosTree

		# Main frame
		self.MainFrame = ROOT.TGVerticalFrame(self, width, height)

		etosTree.SetAnalysisWindow(self)

		# Menu bar
		self.MenuBar = ROOT.TGMenuBar(self, 1, 1, ROOT.kHorizontalFrame)
		self.MenuFile = ROOT.TGPopupMenu();
		self.MenuFile.AddEntry("&Open...", 21)
		
		self.MenuBarItemLayout = ROOT.TGLayoutHints(ROOT.kLHintsTop | ROOT.kLHintsLeft, 0, 4, 0, 0)
		self.MenuBar.AddPopup("&File", self.MenuFile, self.MenuBarItemLayout);
		
		# Add menus for/from the TTree
		for menu in self.etosTree.MenuPixel:
			self.MenuBar.AddPopup(menu[1], menu[0], self.MenuBarItemLayout)
			
		# Canvas 
		self.Canvas = ROOT.TRootEmbeddedCanvas('Canvas', self.MainFrame, 600, 250)
		
		self.MainFrame.AddFrame(self.MenuBar)  # , ROOT.TGLayoutHints())
		self.MainFrame.AddFrame(self.Canvas, ROOT.TGLayoutHints(ROOT.kLHintsExpandX | ROOT.kLHintsExpandY))
		self.AddFrame(self.MainFrame, ROOT.TGLayoutHints(ROOT.kLHintsExpandX | ROOT.kLHintsExpandY))

		# Add bars for/from the TTree
		print self.etosTree.PlotBar
		if self.etosTree.PlotBar!=None:
			self.MainFrame.AddFrame(self.etosTree.PlotBar)
			print "added"
		


		self.SetWindowName('Pixel analysis')
		self.MapSubwindows()
		self.Resize(self.GetDefaultSize())
		self.MapWindow()

		
	# # Destructor
	def __del__(self):
		self.Cleanup()
		
	## Draws the given graph in the requested canvas
	def DrawGraph(self, graph, canvas_name, options="A*"):
		if "first" in canvas_name:
			self.Canvas.GetCanvas().cd(0)
		# If the canvas does not exist yet, resize the window and create it
		if "second" in canvas_name and self.SecondCanvas == None:
			print "Creating canvas"
			self.SecondCanvas = ROOT.TRootEmbeddedCanvas('Canvas', self.MainFrame, 600, 250)
			self.MainFrame.AddFrame(self.SecondCanvas, ROOT.TGLayoutHints(ROOT.kLHintsExpandX | ROOT.kLHintsExpandY))
			self.MapSubwindows()
			self.Resize(self.GetDefaultSize())
			self.MapWindow()
			self.SecondCanvas.GetCanvas().cd(0)
		if "third" in canvas_name and self.ThirdCanvas == None:
			self.ThirdCanvas = ROOT.TRootEmbeddedCanvas('Canvas', self.MainFrame, 600, 250)
			self.MainFrame.AddFrame(self.ThirdCanvas, ROOT.TGLayoutHints(ROOT.kLHintsExpandX | ROOT.kLHintsExpandY))
			self.MapSubwindows()
			self.Resize(self.GetDefaultSize())
			self.MapWindow()
			self.ThirdCanvas.GetCanvas().cd(0)
		
		# Select the proper canvas
		if "second" in canvas_name:
			self.SecondCanvas.GetCanvas().cd(0)
		if "third" in canvas_name:
			self.ThirdCanvas.GetCanvas().cd(0)		
				
		# Draw the graph	
		graph.Draw(options)
		ROOT.gPad.Modified()
		ROOT.gPad.Update()

class EtosTree():

	# # Contructor
	def __init__(self, tree, tree_file):
		
		self.CurveX = None
		self.CurveY = []
		self.Derivative = []
		self.SecondDerivative = []
		self.SCurveGraph = []
		self.DerivativeGraph = []
		self.SecondDerivativeGraph = []
		
		self.gains = None
		self.Multigain = False
		self.PlotBar = None
		
		self.tree = tree
		self.tree_file = tree_file
		
		self.calibration = 0
		self.event = 1
		
		self.tree_type = -1
		

		self.tree_type = self.calibration
		self.pkt_cnt = 10
		self.frame_cnt = 10
		
		self.pixel_menu_dispatch = ROOT.TPyDispatcher(self.handle_menu_pixel)
		self.pdm_menu_dispatch = ROOT.TPyDispatcher(self.handle_menu_pdm)
		self.widgets_dispatch = ROOT.TPyDispatcher(self.handle_widgets)
			
		self.CreateMenu()
		
	## Prepare the GUI bar for adjusting the plot settings
	def PreparePlotBar(self):
		
		if self.Multigain:
			self.PlotBar = ROOT.TGHorizontalFrame(self.AnalysisWindow.MainFrame, self.AnalysisWindow.MainFrame.GetWidth(), 100)
			self.cGains = ROOT.TGComboBox(self.PlotBar)
			self.cGains.SetWidth(100)
			self.cGains.SetHeight(20)
			i=0
			for i,entry in enumerate(self.gains):
				self.cGains.AddEntry(str(int(entry)), i)
			self.cGains.AddEntry("All", len(self.gains))
			self.cGains.Select(len(self.gains))
			
			self.cGains.Connect("Selected(const char*)", "TPyDispatcher", self.widgets_dispatch, "Dispatch()")
			
			self.PlotBar.AddFrame(self.cGains, ROOT.TGLayoutHints())
	
		
	def SetAnalysisWindow(self, window):
		self.AnalysisWindow = window
		# Prepare the GUI gain-to-plot chooser
		self.PreparePlotBar()
		
	def DrawFrame(self, frame_num):
		self.cur_frame = frame_num
		global pdm  # , mainWindow, a
		
		pdm = ROOT.TH2D("pdm", "pdm", 8 * 6, 0, 8 * 6, 8 * 6, 0, 8 * 6)

		pdm.SetStats(0)
		
		count = 0
	
		# Loop through pixels and fill the pdm histogram
		for ipmt in xrange(36):
			for x in xrange(8):
				for y in xrange(8):
					pdm.SetBinContent(int(x + ipmt % 6 * 8) + 1, int(y + ipmt / 6 * 8) + 1, np.random.randint(10, size=1)[0])
					count+=1
		
		pdm.SetDirectory(0)
		pdm.Draw("colz")
		# Set some margins of the histogram, otherwise it is difficult to click pixels on the edges
		pdm.GetXaxis().SetRangeUser(-1, 49)
		pdm.GetYaxis().SetRangeUser(-1, 49)
		ROOT.gPad.Update()
		
	def Draw(self, command1, command2="", command3=""):
		self.tree.Draw(command1, command2, command3)
		
	def CreateMenu(self):
		# Menu bars
		self.MenuPixelAnalysis = ROOT.TGPopupMenu();
		
		# Create menu for calibration TTree
		if self.tree_type == self.calibration:
			self.MenuPixelAnalysis.AddEntry("&Derivative...", treeWidgetsIds.mDerivative)
		
		self.MenuPixelAnalysis.Connect("Activated(Int_t)", "TPyDispatcher", self.pixel_menu_dispatch, "Dispatch()")
		
		# Put the menu's into list in case of multiple main menu's in the future
		self.MenuPixel = []
		self.MenuPixel.append((self.MenuPixelAnalysis, "&Analysis"))

	def handle_menu_pixel(self):
		menuId = self.MenuPixelAnalysis.GetCurrent().GetEntryId()
		print self.MenuPixelAnalysis.GetCurrent().GetEntryId() == treeWidgetsIds.mDerivative
		
		if menuId == treeWidgetsIds.mDerivative:		
			self.CalculateDerivative()
			
	def handle_menu_pdm(self):
		menuId = self.MenuPdmAnalysis.GetCurrent().GetEntryId()
			
	def handle_widgets(self):
	 	wid = ROOT.BindObject( ROOT.gTQSender, ROOT.TGComboBox )
		self.GainRedraw(wid.GetSelectedEntry().GetText().GetString())
		
	def GainRedraw(self, gain):
		# If the new selected gain is the same as previously selected, do nothing
		if gain==self.gain: return
			
	def CalculateDerivative(self, draw=True):
		# For each possible gain
		for curve_idx, CurveY in enumerate(self.CurveY):
			self.Derivative.append(np.arange(0, 1.025, 0.005))
				
			# Generate graph for the derivative
			self.DerivativeGraph.append(ROOT.TGraph(len(self.Derivative[-1]), np.arange(0.,1025,5.).astype(np.float64), self.Derivative[-1].astype(np.float64)))
			#self.DerivativeGraph.append(ROOT.TGraph(len(self.Derivative[-1]), np.arange(0.,102.5,0.5).astype(np.float64), self.Derivative[-1].astype(np.float64)))

			self.AnalysisWindow.DrawGraph(self.DerivativeGraph[-1], "second")							

		

		
# # Choose the TTree to work on from the TFile
def ChooseTree(ftrees):
	etosTree = EtosTree("uuu", ftrees)
	return etosTree

def main():
	# Choose the tree from the TFile
	etosTree = ChooseTree("haha")
	
	# Open the main window
	mainWindow = pMainFrame(ROOT.gClient.GetRoot(), 400, 400, etosTree)
	
	# Draw the first GTU/threshold from tree
	
	mainWindow.DrawFrame(15)

	print pdm
	print ROOT.gPad
	
	return mainWindow
	
if __name__ == '__main__':
	a = main()

	rep = ''
	while not rep in [ 'q', 'Q' ]:
		rep = raw_input('enter "q" to quit: ')
		if 1 < len(rep):
			rep = rep[0]

