import logging

from pipython import GCSDevice

from PyQt5 import QtCore
from PyQt5 import uic
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QGridLayout
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QTextBrowser
from PyQt5.QtWidgets import QDesktopWidget
from PyQt5.QtWidgets import QSplashScreen
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QThread
from PyQt5.QtCore import pyqtSignal
from AppMap_setup import Appmap

import sys
import os, time

#python -m PyQt5.uic.pyuic PI_GUI.ui -o PI_GUI.py
#python -m PyQt5.uic.pyuic PI_GUI_App_Map.ui -o PI_GUI_App_Map.py

# logger=logging.getLogger(__name__)
# QTextBrowser.append(logging.Handler)
# handler=
logging.basicConfig(format='%(pathname)s, %(filename)s, %(lineno)s, %(funcName)s, %(message)s',level=logging.INFO)

#####Monkey Patch
def AddnRefresh(self,content):
    self.append(content)
    self.verticalScrollBar().setValue(self.verticalScrollBar().maximum())
QTextBrowser.AddnRefresh=AddnRefresh
#####Class: Subwindow in Child Thread
class Subwins(QThread):  #Class for creating a new window thread
    move_request=pyqtSignal(int)
    def __init__(self,target_class):
        super().__init__()
        self.target_class=target_class
    def run(self):
        self.win=QWidget()
        self.ui=self.target_class(self.win,self.move_request)
        self.win.show()
#####Class: Micropositioner Connect and Control in Child Thread
class PIDevice(QThread):  #Class for initialising the micropositioner control thread
    connecting=pyqtSignal(str) # Connecting signal
    referencing=pyqtSignal(str) # Referencing signal
    devicelist=pyqtSignal(list)  #Output device to be controlled by the parent class
    errorupload=pyqtSignal(str) #Output error
    def __init__(self):
        super().__init__()
    def run(self):
        STAGES = ['M-605.1DD']  # Connect stages to axes
        REFMODES = ['FRF']  # Reference the connected stages
        devdic={0:'X',1:'Y',2:'Z'}
        devlist=devx=devy=devz=None
        errlist=[]
        try:
            logging.info('Connection thread trying to connect')
            self.connecting.emit('Z')
            devz=GCSDevice('C-863')
            devz.OpenRS232DaisyChain(comport=8, baudrate=9600)
            chainid=devz.dcid
            devz.ConnectDaisyChainDevice(3, chainid)
            self.connecting.emit('Y')
            devy=GCSDevice('C-863')
            devy.ConnectDaisyChainDevice(2, chainid)
            devx=GCSDevice('C-863')
            self.connecting.emit('X')
            devx.ConnectDaisyChainDevice(1, chainid)
        except Exception as arg_err:
            self.errorupload.emit(str(arg_err))
            errlist.append(arg_err)
        devlist=[devx,devy,devz]
        if None not in devlist and errlist==[]:
            logging.info('All device found, connection thread trying to reference')
        if errlist==[]:
            self.devicelist.emit(devlist)
#####Class: Main GUI
class Mainwin(QMainWindow): 
    def __init__(self):
        super().__init__()
        self.ui=uic.loadUi('PI_GUI.ui',self)
        self.devlist=[]
        self.threadlist=[]
        self.devstring={0:'X',1:'Y',2:'Z'}
        #Splash Session (Connecting to Devices)
        splash_img = QPixmap('splash.png')
        self.splash = QSplashScreen(splash_img, QtCore.Qt.WindowStaysOnTopHint)
        self.splash.show()
        QApplication.processEvents()
        time.sleep(1)
        self.splash.showMessage('starting...',color=QColor('white'))
        QApplication.processEvents()
        time.sleep(1)
        self.main()
        self.Connect_Dev()
        self.splash.close()
        QApplication.processEvents()
        self.ServoInitialise()
    def main(self):
        self.setWindowTitle('Microuija')
        self.setWindowIcon(QIcon('icon.png'))
        self.resize(1500,300)
        #Widget Wrapping
        self.labelaxiset=[self.label_xaxis,self.label_yaxis,self.label_zaxis]
        self.btnlset=[self.pushButton_xl,self.pushButton_yl,self.pushButton_zl]
        self.btnrset=[self.pushButton_xr,self.pushButton_yr,self.pushButton_zr]
        self.lineEdit_axisset=[self.lineEdit_x,self.lineEdit_y,self.lineEdit_z]
        self.lineEdit_tpset=[self.lineEdit_xtp,self.lineEdit_ytp,self.lineEdit_ztp]
        self.lineEdit_ssset=[self.lineEdit_xss,self.lineEdit_yss,self.lineEdit_zss]
        self.lineEdit_velset=[self.lineEdit_xvel,self.lineEdit_yvel,self.lineEdit_zvel]
        self.servoset=[self.checkBox_x,self.checkBox_y,self.checkBox_z]
        #Widget Style Setting
        self.pushButton_halt.setStyleSheet("background-color: red")
        self.lineEdit_x.setText('0.0000')
        self.lineEdit_y.setText('0.0000')
        self.lineEdit_z.setText('0.0000')
        self.lineEdit_x.setFont(QFont('MS Shell Dlg 2',26))
        self.lineEdit_y.setFont(QFont('MS Shell Dlg 2',26))
        self.lineEdit_z.setFont(QFont('MS Shell Dlg 2',26))
        self.checkBox_x.setFont(QFont('Roboto',10))
        self.checkBox_y.setFont(QFont('Roboto',10))
        self.checkBox_z.setFont(QFont('Roboto',10))
        self.lineEdit_x.setAlignment(Qt.AlignCenter)
        self.lineEdit_y.setAlignment(Qt.AlignCenter)
        self.lineEdit_z.setAlignment(Qt.AlignCenter)
        self.textBrowser_log.setFont(QFont('Roboto',10))
        self.textBrowser_log.setText('Program Log：\n')
        self.pushButton_halt.setFont(QFont('Roboto',16))
        self.pushButton_appmap.setFont(QFont('Roboto',16))
        #GUI Initialising
        if None in self.devlist:
            for w in self.findChildren(QPushButton):
                w.setEnabled(False)
            self.pushButton_appmap.setEnabled(False)
        #Signal-Slot Binding
        self.actionconnect.triggered.connect(self.Connect_Dev)
        self.pushButton_appmap.clicked.connect(self.Run_Appmap)
        self.pushButton_xl.clicked.connect(
            lambda: self.MoveTo(0,'-'+self.lineEdit_xss.text(),self.lineEdit_xvel.text()))
        self.pushButton_yl.clicked.connect(
            lambda: self.MoveTo(1,'-'+self.lineEdit_yss.text(),self.lineEdit_yvel.text()))
        self.pushButton_zl.clicked.connect(
            lambda: self.MoveTo(2,'-'+self.lineEdit_zss.text(),self.lineEdit_zvel.text()))
        self.pushButton_xr.clicked.connect(
            lambda: self.MoveTo(0,self.lineEdit_xss.text(),self.lineEdit_xvel.text()))
        self.pushButton_yr.clicked.connect(
            lambda: self.MoveTo(1,self.lineEdit_yss.text(),self.lineEdit_yvel.text()))
        self.pushButton_zr.clicked.connect(
            lambda: self.MoveTo(2,self.lineEdit_zss.text(),self.lineEdit_zvel.text()))
        self.pushButton_xgo.clicked.connect(
            lambda: self.GoTo(0,self.lineEdit_xtp.text(),self.lineEdit_xvel.text())
        )
        self.pushButton_ygo.clicked.connect(
            lambda: self.GoTo(1,self.lineEdit_ytp.text(),self.lineEdit_yvel.text())
        )
        self.pushButton_zgo.clicked.connect(
            lambda: self.GoTo(2,self.lineEdit_ztp.text(),self.lineEdit_zvel.text())
        )
        self.pushButton_halt.clicked.connect(self.Halt)
        self.checkBox_x.clicked.connect(lambda: self.Servo(0))
        self.checkBox_y.clicked.connect(lambda: self.Servo(1))
        self.checkBox_z.clicked.connect(lambda: self.Servo(2))

#####GUI Operation
    def GUI_Activate(self,devlist): # Pass devices to main thread, activate main GUI
        self.devlist=devlist
        self.textBrowser_log.AddnRefresh('Connected')
        for w in self.findChildren(QPushButton):
            w.setEnabled(True)
        for w in self.lineEdit_ssset:
            w.setText('1')
        for w in self.lineEdit_velset:
            w.setText('2')
            self.ShowPos()
    def ShowPos(self):
        try:
            self.lineEdit_x.setText(str(format(self.devlist[0].qPOS('1')['1'],'.4f')))
            self.lineEdit_y.setText(str(format(self.devlist[1].qPOS('1')['1'],'.4f')))
            self.lineEdit_z.setText(str(format(self.devlist[2].qPOS('1')['1'],'.4f')))
        except Exception as arg_err:
            print(arg_err)
            self.ShowPos()
    def Run_Appmap(self):
        self.appmap_win=Appmap(self)
        self.appmap_win.show()
    def closeEvent(self,event):
        print('Closing')
        for dev in self.devlist:
            dev.CloseConnection()
        for thread in self.threadlist:
            thread.terminate()

#####Micropositioner Operation
    def Connect_Dev(self): # Establish micropositioners conection in a new thread
        for w in self.findChildren(QPushButton):
            w.setEnabled(False)
        self.dev_thrd=PIDevice()
        self.dev_thrd.connecting.connect(self.Connecting)
        self.dev_thrd.referencing.connect(self.Referencing)
        self.dev_thrd.devicelist.connect(self.GUI_Activate)
        self.dev_thrd.errorupload.connect(self.ConnectError)
        self.dev_thrd.start()
        self.dev_thrd.wait()
    def ServoInitialise(self):
        self.textBrowser_log.AddnRefresh('Initialising Servo')
        try: 
            for devnum in range(len(self.devlist)):
                if self.devlist[devnum].qSVO()['1']:
                    self.textBrowser_log.AddnRefresh('axis '+str(devnum)+' checked')
                    self.servoset[devnum].setChecked(True)
                    self.labelaxiset[devnum].setStyleSheet('color: lime')
                else:
                    self.servoset[devnum].setChecked(False)
                    self.labelaxiset[devnum].setStyleSheet('color: red')
        except Exception as arg_err:
            self.textBrowser_log.AddnRefresh(str(arg_err))
    def Connecting(self,axis):
        self.textBrowser_log.AddnRefresh(axis+' Axis'+'Connecting...')
        self.splash.showMessage(axis+' Axis'+'Connecting...',color=QColor('white'))
        QApplication.processEvents()
    def Referencing(self,axis):
        self.textBrowser_log.AddnRefresh(axis+' Axis '+'Referencing...')
        self.splash.showMessage(axis+' Axis'+'Referencing...',color=QColor('white'))
    def ConnectError(self,error):
        self.errormsg=QMessageBox()
        self.errormsg.setIcon(QMessageBox.Critical)
        self.errormsg.setText('Device Error:\n'+str(error))
        self.errormsg.setStandardButtons(QMessageBox.Ok)
        self.errormsg.setWindowTitle('Error')
        self.errormsg.show()
        self.textBrowser_log.AddnRefresh(str(error))
    def Servo(self, axis):
        if self.servoset[axis].isChecked()==True:
            try:
                self.devlist[axis].SVO(1,True)
                if self.devlist[axis].qSVO()['1']==True:
                    self.labelaxiset[axis].setStyleSheet('color: lime')
                    self.servoset[axis].setChecked(True)
                else: 
                    self.labelaxiset[axis].setStyleSheet('color: red')
                    self.servoset[axis].setChecked(False)
            except Exception as arg_err:
                self.textBrowser_log.AddnRefresh(str(arg_err))
        else:
            try:
                self.devlist[axis].SVO(1,False)
                if self.devlist[axis].qSVO()['1']==True:
                    self.labelaxiset[axis].setStyleSheet('color: lime')
                    self.servoset[axis].setChecked(True)
                else: 
                    self.labelaxiset[axis].setStyleSheet('color: red')
                    self.servoset[axis].setChecked(False)
            except Exception as arg_err:
                self.textBrowser_log.AddnRefresh(str(arg_err))
    def Halt(self):
        for thread in self.threadlist:
            thread.quit()
            thread.wait()
        for dev in self.devlist:
            dev.HLT('1',noraise=True)
        for w in self.findChildren(QPushButton):
            w.setEnabled(False)
        QApplication.processEvents()
        self.textBrowser_log.AddnRefresh(
            "<span style=\" font-size:10pt; font-weight:600; color:#ff0000;\" >"
            'HALT!'
            "</span>")
        for devn in range(len(self.devlist)):
            self.labelaxiset[devn].setStyleSheet('color: red')
        QApplication.processEvents()
        self.textBrowser_log.AddnRefresh('Terminating threads...')
        for devn in range(len(self.devlist)):
            self.OnTarget(self.devlist[devn],self.lineEdit_axisset[devn],self.labelaxiset[devn])
        QApplication.processEvents()
        for w in self.findChildren(QPushButton):
            w.setEnabled(True)
        self.textBrowser_log.AddnRefresh('Back Online')
    def MoveTo(self,index,step='0',vel='2'):
        Label=self.labelaxiset[index]
        step=float(step)
        vel=float(vel)
        Label.setStyleSheet('color:yellow')
        QApplication.processEvents()
        cpos=float(format(self.devlist[index].qPOS('1')['1'],'.4f'))
        tpos=cpos+step
        self.textBrowser_log.AddnRefresh('axis '+self.devstring[index]+': '+str(cpos) +' to '+ str(tpos))
        self.devlist[index].VEL('1',vel)
        self.devlist[index].MVR('1',step)
        self.OnTarget(self.devlist[index],self.lineEdit_axisset[index],Label)
    def GoTo(self,index,tpos,vel):
        Label=self.labelaxiset[index]
        tpos=float(format(float(tpos),'.4f'))
        cpos=float(format(self.devlist[index].qPOS('1')['1'],'.4f'))
        vel=float(vel)
        Label.setStyleSheet('color:yellow')
        QApplication.processEvents()
        self.textBrowser_log.AddnRefresh('axis '+self.devstring[index]+': '+str(format(cpos,'.4f')) +' to '+ str(tpos))
        self.devlist[index].VEL('1',vel)
        self.devlist[index].MVE('1',tpos)
        self.OnTarget(self.devlist[index],self.lineEdit_axisset[index],Label)
    def OnTarget(self,dev,LineEdit,Label):
        err=1
        ontcount=0
        while ontcount<5 or err!=0:
            pos1=dev.qPOS('1')['1']
            time.sleep(0.2)
            pos2=dev.qPOS('1')['1']
            err=abs(pos2-pos1)
            LineEdit.setText(str(format(dev.qPOS('1')['1'],'.4f')))
            if err<0.0001:
                ontcount+=1
            QApplication.processEvents()
        LineEdit.setText(str(format(dev.qPOS('1')['1'],'.4f')))
        Label.setStyleSheet('color:lime')

    def ServoCheck(self):
        permission=True
        for dev in self.devlist:
            if dev.qSVO()['1']==False:
                permission=False
        if permission==True:
            self.appmap_win.permission=True

##### Handlers
    def Receiver(self,para):
         self.MoveTo(para[0],para[1],abs(para[1])+0.001)




if __name__=='__main__':
    app = QApplication(sys.argv)
    # win=QMainWindow()
    # ui=mainwin(win)
    win=Mainwin()
    win.show()
    sys.exit(app.exec_())

        