#!/usr/bin/python
'''
Lists the various available experiments and allows users to run them
'''

from __future__ import print_function
from argparse import ArgumentParser
import sys,time

parser = ArgumentParser()
# Add more options if you like
parser.add_argument("-P", dest="PortName", help="If you have connected multiple devices, provide the port name . e.g /dev/ttyACM0", metavar="PORT_NAME")
args = parser.parse_args()

import os
os.environ['QT_API'] = 'pyqt'
import sip
sip.setapi("QString", 2)
sip.setapi("QVariant", 2)
from PyQt4 import QtCore, QtGui
from PyQt4 import QtWebKit

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

from SEEL_Apps.stylesheets import styles
from SEEL_Apps.utilitiesClass import utilitiesClass
from SEEL_Apps.templates import single_col_exp

import os,string,time,pkgutil,importlib,functools,pkg_resources,serial.tools.list_ports

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

class MyMainWindow(QtGui.QMainWindow, single_col_exp.Ui_MainWindow,utilitiesClass):
	def __init__(self, parent=None,**kwargs):
		super(MyMainWindow, self).__init__(parent)
		self.eventHandler = kwargs.get('app',None)
		self.showSplash();self.updateSplash(10,'Setting up UI...')


		self.setupUi(self)
		self.styleText = pkg_resources.resource_string('SEEL_Apps', "stylesheets/overall.css").decode("utf-8")
		self.setStyleSheet(self.styleText)
		self.updateSplash(10,'Fetching libraries...')

		from SEEL import interface
		self.row=0;	self.col=0;	self.colLimit=3

		self.updateSplash(10,'Connecting to Device...')
		if  args.PortName: self.I = interface.Interface(port = args.PortName)  #Not using connect method because it returns None if unconnected,
		else: self.I = interface.Interface(verbose=True)                       #and I need to pass the methods of interface to various control widgets regardless
		self.hexid=''
		try:
			if not self.I.connected:
				if len(self.I.H.occupiedPorts):
					diag = QtGui.QMessageBox.about(self,'Error','Could not find available device.\nSoftware already running for ports: %s'%self.I.H.occupiedPorts)
				else:
					diag = QtGui.QMessageBox.about(self,'Error','Could not find available device')
				#diag.show()
				self.styleText = pkg_resources.resource_string('SEEL_Apps', "stylesheets/overall_disconnected.css").decode("utf-8")
				self.setStyleSheet(self.styleText)
				self.setWindowTitle('Error : Not Connected')
			else:
				self.hexid = hex(self.I.device_id()&0xFFFF)
		except:
			self.updateSplash(30,'Connection Error!')
			#self.SCF1.setStyleSheet(_fromUtf8(styles.disconnected))
			self.styleText = pkg_resources.resource_string('SEEL_Apps', "stylesheets/overall_disconnected.css").decode("utf-8")
			self.setStyleSheet(self.styleText)
			pass

		self.ExperimentLayout.setAlignment(QtCore.Qt.AlignTop|QtCore.Qt.AlignLeft)
		self.WidgetLayout.setAlignment(QtCore.Qt.AlignTop|QtCore.Qt.AlignLeft)
		self.advancedControlsLayout.setAlignment(QtCore.Qt.AlignTop|QtCore.Qt.AlignLeft)

		#Widgets related to power supplies PV1,PVS2,PV3,PCS
		self.supplySection = self.supplyWidget(self.I);		self.advancedControlsLayout.addWidget(self.supplySection)
		
		#Widgets related to Analog Waveform generators
		self.sineSection = self.sineWidget(self.I); 		self.advancedControlsLayout.addWidget(self.sineSection)

		#Widgets related to Digital Waveform generators
		self.pwmSection = self.pwmWidget(self.I);   		self.advancedControlsLayout.addWidget(self.pwmSection)
		
		self.tmpTimer = QtCore.QTimer()
		self.tmpTimer.singleShot(2,functools.partial(self.loadIcons,'SEEL_Apps.apps'))
		#self.loadIcons('SEEL_Apps.apps')
		

		row=0;col=0;colLimit=3
		self.funcs=[]

		autogenControls=[]
		autogenControls.append({'TITLE':'Wave 1','MIN':1,'MAX':5000,'FUNC':self.I.set_sine1,'TYPE':'dial','UNITS':'Hz','TOOLTIP':'Frequency of waveform generator #1','LINK':self.updateWAVE1_FREQ})
		autogenControls.append({'TITLE':'Wave 2','MIN':1,'MAX':5000,'FUNC':self.I.set_sine2,'TYPE':'dial','UNITS':'Hz','TOOLTIP':'Frequency of waveform generator #2','LINK':self.updateWAVE2_FREQ})
		autogenControls.append({'TITLE':'square 1','MIN':0,'MAX':50000,'FUNC':self.modifiedSqr1,'TYPE':'dial','UNITS':'Hz','TOOLTIP':'Frequency of square wave generator #1\n0 for switched off, Max for On state'})

		tmpfunc = functools.partial(self.I.DAC.__setRawVoltage__,'PV1')
		autogenControls.append({'TITLE':'PV1','MIN':0,'MAX':4095,'FUNC':tmpfunc,'TYPE':'dial','UNITS':'V','TOOLTIP':'Programmable Voltage Source ','LINK':self.updatePV1_LABEL})

		tmpfunc = functools.partial(self.I.DAC.__setRawVoltage__,'PV2')
		autogenControls.append({'TITLE':'PV2','MIN':0,'MAX':4095,'FUNC':tmpfunc,'TYPE':'dial','UNITS':'V','TOOLTIP':'Programmable Voltage Source ','LINK':self.updatePV2_LABEL})

		tmpfunc = functools.partial(self.I.DAC.__setRawVoltage__,'PV3')
		autogenControls.append({'TITLE':'PV3','MIN':0,'MAX':4095,'FUNC':tmpfunc,'TYPE':'dial','UNITS':'V','TOOLTIP':'Programmable Voltage Source ','LINK':self.updatePV3_LABEL})

		tmpfunc = lambda x: self.I.DAC.__setRawVoltage__('PCS',4095-x)
		autogenControls.append({'TITLE':'PCS','MIN':0,'MAX':4095,'FUNC':tmpfunc,'TYPE':'dial','UNITS':'mA','TOOLTIP':'Programmable Current Source ','SCALE_FACTOR' : 1e3,'LINK':self.updatePCS_LABEL})

		autogenControls.append({'TITLE':'CAPACITANCE','FUNC':self.I.get_capacitance,'TYPE':'button','UNITS':'F','TOOLTIP':'Read Capacitance connected to CAP input '})

		tmpfunc = functools.partial(self.I.get_average_voltage,samples=100)
		autogenControls.append({'TITLE':'VOLTMETER','FUNC':tmpfunc,'TYPE':'selectButton','UNITS':'V','TOOLTIP':'Voltmeter','OPTIONS':self.I.allAnalogChannels})
		autogenControls.append({'TITLE':'Low Frequency','FUNC':self.I.get_freq,'TYPE':'selectButton','UNITS':'Hz','TOOLTIP':'Measure Frequency. Minimum 40Hz','OPTIONS':self.I.allDigitalChannels})
		autogenControls.append({'TITLE':'High Frequency','FUNC':self.I.get_high_freq,'TYPE':'selectButton','UNITS':'Hz','TOOLTIP':'Measure Frequencies over 1MHz with 10Hz resolution','OPTIONS':self.I.allDigitalChannels})

		autogenControls.append({'TITLE':'SR-04 Distance','FUNC':self.I.estimateDistance,'TYPE':'button','UNITS':'m','TOOLTIP':'Measure Distance using an HCSR04 sensor. TRIG-SQR1  , ECHO-ID1'})

		self.setWindowTitle(self.I.generic_name + ' : '+self.I.H.version_string.decode("utf-8")+' - '+self.hexid)

		for C in autogenControls:
			if C['TYPE']=='dial':
				self.funcs.append(C.get('FUNC',None))
				self.WidgetLayout.addWidget(self.dialIcon(**C),row,col)
			elif C['TYPE']=='button':
				self.funcs.append(C.get('FUNC',None))
				self.WidgetLayout.addWidget(self.buttonIcon(**C),row,col)
			elif C['TYPE']=='selectButton':
				self.funcs.append(C.get('FUNC',None))
				self.WidgetLayout.addWidget(self.selectAndButtonIcon(**C),row,col)

			col+=1
			if(col==colLimit):
				col=0;row+=1

		self.WidgetLayout.addWidget(self.setStateIcon(I=self.I),row,col)
		col+=1
		if(col==colLimit):
				col=0;row+=1

		self.menu_entries=[]
		self.menu_group=None

		self.helpView = QtWebKit.QWebView()
		self.helpLayout.addWidget(self.helpView)

		self.shortlist=[]
		self.timer = QtCore.QTimer()
		self.timer.timeout.connect(self.locateDevices)
		self.timer.start(500)
		self.updateSplash(30,'Almost done...')
		#self.splash.finish(self)
		self.runningApp = None


	def loadBasicExperiments(self):
		print('basic')
		self.clearExperimentIcons()
		self.loadIcons('SEEL_Apps.apps')
		
	def loadAdvancedExperiments(self):
		print('advanced')

	def modifiedSqr1(self,val):
		if val<4:
			self.I.set_state(SQR1=0)
			return 'LOW'
		elif val==50000:
			self.I.set_state(SQR1=1)
			return 'HIGH'
		else:
			v = self.I.sqr1(val)
			return v


	def loadList(self,basepackage):
		self.funcs=[]
		baselib = importlib.import_module(basepackage)
		apps = [name for _, name, _ in pkgutil.iter_modules([os.path.dirname(baselib.__file__)])]
		print(apps)
		self.colLimit=1
		for app in apps:
			if(self.col==self.colLimit):
				self.col=0;self.row+=1
			fn = functools.partial(self.launchFunc,basepackage+'.'+app)
			self.funcs.append(fn)
			icon = self.experimentListItem(basepackage,app,fn)
			self.ExperimentLayout.addWidget(icon,self.row,self.col)
			icon.mouseHover.connect(self.setHint)
			self.col+=1

		self.col=0;self.row+=1
		line = QtGui.QFrame()
		line.setFrameShape(QtGui.QFrame.HLine)
		line.setFrameShadow(QtGui.QFrame.Sunken)
		self.ExperimentLayout.addWidget(line,self.row,0,1,3)
		self.row+=1
		
	def loadIcons(self,basepackage):
		self.funcs=[]
		baselib = importlib.import_module(basepackage)
		subdirs = [name for _, name,isDir in pkgutil.iter_modules([os.path.dirname(baselib.__file__)]) if isDir==True]
		print (subdirs)
		for sub in subdirs:
			submodule = basepackage+'.'+sub
			sublib = importlib.import_module(submodule)
		
			title = QtGui.QLabel();	title.setText(sublib.__dict__.get('title',sub[1:])); self.ExperimentLayout.addWidget(title,self.row,0,1,self.colLimit)
			self.col=0;self.row+=1
			line = QtGui.QFrame();	line.setFrameShape(QtGui.QFrame.HLine);	line.setFrameShadow(QtGui.QFrame.Sunken); self.ExperimentLayout.addWidget(line,self.row,0,1,self.colLimit)
			self.col=0;self.row+=1

			apps = [name for _, name,isDir in pkgutil.iter_modules([os.path.dirname(sublib.__file__)]) if isDir==False]
			for app in apps:
				if(self.col==self.colLimit):
					self.col=0;self.row+=1
				fn = functools.partial(self.launchFunc,submodule+'.'+app)
				self.funcs.append(fn)
				icon = self.experimentIcon(submodule,app,fn)
				self.ExperimentLayout.addWidget(icon,self.row,self.col)
				icon.mouseHover.connect(self.setHint)
				self.col+=1

			self.col=0;self.row+=1

	def clearExperimentIcons(self):
		while self.ExperimentLayout.count():
			item = self.ExperimentLayout.takeAt(0)
			item.widget().deleteLater()
		self.row=0;
		self.col=0;
		self.colLimit=3

	def setHint(self,t):
		self.hintText.setHtml(t)

	def launchFunc(self,fname):
		if self.I:
			if fname.split('.')[1]=='apps':
				if self.runningApp:
					self.runningApp.close()

			FILE = importlib.import_module(fname)
			inst = FILE.AppWindow(self,I=self.I)
			inst.show()
			size = inst.geometry()
			inst.setGeometry(300, 50,size.width(), size.height())

			#Load help HTML if found
			if fname.split('.')[1]=='apps':
				try:
					HF = FILE.params.get('helpfile','introduction.html')
					print(HF)
					if 'http' in HF:
						helpurl = HF
					else:
						helpurl = pkg_resources.resource_filename('SEEL_Apps',os.path.join('helpfiles',HF))
					print('url',helpurl)
					self.helpView.setUrl(QtCore.QUrl(helpurl))          
					self.tabWidget.setCurrentIndex(1)
				except:
					print('No help URL file found')


			if fname.split('.')[1]=='apps':
				self.runningApp = inst
		else:
			print(self.setWindowTitle('Device Not Connected!'))

	def returnToApps(self):
		self.tabWidget.setCurrentIndex(0)
		
	def launchAboutDevice(self):
		print ('about')
		if self.I:
			from SEEL_Apps.utilityApps import deviceInfo
			info = deviceInfo.AppWindow(self,I=self.I)
			info.show()
		else:
			print (self.setWindowTitle('Device Not Connected!'))

	def locateDevices(self):
		L = serial.tools.list_ports.comports()
		shortlist=[]
		for a in L:
			if ('ACM' in a[1]) and ('04d8:00df' in a[2]):
				shortlist.append(a)

		total = len(shortlist)
		if shortlist != self.shortlist:
			self.shortlist=shortlist
			for a in self.menu_entries:
				self.deviceCombo.removeItem(0)
			self.menu_entries=[]
			for a in shortlist:
				self.deviceCombo.addItem(a[0])
				self.menu_entries.append(a[0])


	def selectDevice(self):
		sel = self.deviceCombo.currentText()
		if ( not ('ACM' in sel  or 'USB' in sel)):
			self.displayDialog('No devices Found')
			return
		self.splash.show();self.progressBar.setValue(0)
		self.updateSplash(20,'Reconnecting to %s'%(sel));
		self.updateSplash(20,'Reconnecting to %s'%(sel));
		if self.I:
			self.I.reconnect(port = sel)
			self.updateSplash(20,'Finished Reconnecting...');
			self.displayDialog('Reconnected :'+self.I.H.version_string.decode("utf-8"))
			#self.SCF1.setStyleSheet(_fromUtf8(styles.connected))
			self.styleText = pkg_resources.resource_string('SEEL_Apps', "stylesheets/overall.css").decode("utf-8")
			self.setStyleSheet(self.styleText)
			self.setWindowTitle('SEELablet : '+self.I.H.version_string.decode("utf-8"))
		else:
			self.I = interface.connect(port = sel)
			self.updateSplash(20,'Finished Loading...');
			self.displayDialog('Connected :'+self.I.H.version_string.decode("utf-8"))
			try:
				#self.SCF1.setStyleSheet(_fromUtf8(styles.connected))
				self.styleText = pkg_resources.resource_string('SEEL_Apps', "stylesheets/overall.css").decode("utf-8")
				self.setStyleSheet(self.styleText)
				self.setWindowTitle('SEELablet : '+self.I.H.version_string.decode("utf-8"))
			except:
				pass
		self.splash.close()
		return

	def updateSplash(self,x,txt=''):
		self.progressBar.setValue(self.progressBar.value()+x)
		if(len(txt)):self.splashMsg.setText('  '+txt)
		self.eventHandler.processEvents()
		self.splash.repaint()

	def showSplash(self):
		splash_pix = QtGui.QPixmap(pkg_resources.resource_filename('SEEL_Apps.stylesheets', "splash.png"))
		self.splash = QtGui.QSplashScreen(splash_pix)# QtCore.Qt.WindowStaysOnTopHint)
		# adding progress bar
		self.progressBar = QtGui.QProgressBar(self.splash); self.progressBar.move(0,self.splash.height()-20)
		self.splashMsg = QtGui.QLabel(self.splash);self.splashMsg.setStyleSheet("font-weight:bold;color:white")
		self.progressBar.resize(self.splash.width(),20)
		self.splashMsg.setText('Loading....');self.splashMsg.resize(self.progressBar.width(),20) ; self.splashMsg.move(0,self.splash.height()-20)
		css = pkg_resources.resource_string('SEEL_Apps', "stylesheets/splash.css").decode("utf-8")
		if css:
			self.splash.setStyleSheet(css)
		self.splash.setMask(splash_pix.mask())
		self.splash.show()


	def resetDevice(self):
		if self.I:
			if self.I.connected:
				self.I.resetHardware()
				self.I.H.fd.close()
				self.I.reconnect()

	def __del__(self):
		try:
			self.I.H.fd.close()
		except:
			pass
		print('bye')

	############################Section for correlating control widgets#################################

	def updateWAVE1_FREQ(self,value,units=''):
		self.sineSection.WAVE1_FREQ.setText('%.3f %s '%(value,units))
	def updateWAVE2_FREQ(self,value,units=''):
		self.sineSection.WAVE2_FREQ.setText('%.3f %s '%(value,units))
	def updatePV1_LABEL(self,value,units=''):
		self.supplySection.PV1_LABEL.setText('%.3f %s '%(value,units))
	def updatePV2_LABEL(self,value,units=''):
		self.supplySection.PV2_LABEL.setText('%.3f %s '%(value,units))
	def updatePV3_LABEL(self,value,units=''):
		self.supplySection.PV3_LABEL.setText('%.3f %s '%(value,units))
	def updatePCS_LABEL(self,value,units=''):
		self.supplySection.PCS_LABEL.setText('%.3f %s '%(value,units))

	def measure_dcycle(self):
		inp = self.timing_input.currentText()
		v=self.I.DutyCycle(inp)
		if(v[0]!=-1):p=100*v[1]
		else: p=0
		self.timing_results.setText('Duty Cycle: %f %%'%(p))

	def measure_interval(self):
		t = self.I.MeasureInterval(self.edge1chan.currentText(),self.edge2chan.currentText(),self.edge1edge.currentText(),self.edge2edge.currentText())
		self.time_interval_label.setText('time: %.2e S'%(t))

	def startRemoteServer(self):
		try:
			from SEEL_Apps.utilityApps import remote
			import inspect

			funcs=dir(self.I)
			self.methods={}
			self.function_list=[]
			for a in funcs:
				fn=getattr(self.I,a)
				try:
					args=inspect.getargspec(fn).args
				except:
					args=[]

				if len(args)>0:
					if inspect.ismethod(fn):
						self.methods[a]=(fn,args)		#list of tuples of all methods in device handler
						if args[0]=='self': self.function_list.append([a,args[1:] ])

			print (self.function_list)
			print (self.methods)


			self.remote = remote.CherryPyClass(self.function_list,self.methods)
			self.remoteThread = remote.CherryPyThread(self.remote)
			#thread.finished.connect(app.exit)
			self.remoteThread.start()
			QtGui.QMessageBox.about(self,'Server Ready',"Try accessing localhost:8080/get_voltage('CH1') from your web browser.\n Do not access the device from multiple locations simultaneously.")
			
		except:
			pass

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myapp = MyMainWindow(app=app)
    myapp.show()
    myapp.splash.finish(myapp)
    sys.exit(app.exec_())
