#!/usr/bin/python3
# -*- coding: utf-8 -*-
# script CosmoGui.py
from __future__ import print_function, division, unicode_literals
from __future__ import absolute_import
from builtins import super
''' 
  A GUI to control runCosmo.py 

    - select and edit configuration files 
    - select working direcotory
    - start data taking via runCosmo.py
'''

import sys, os, time, yaml, threading, subprocess
from PyQt5.QtWidgets import QMessageBox

from .CosmoGuiUi import * # import code generated by designer-qt5

CONFIG_ENVIRONMENT_file = 'picocosmo.cfg'

class ComoGuiUiInterface(Ui_CosmoWindow):
    '''interface to class generated by designer-qt5
    '''   

    def MB_Question(self, Title, Text):
    # wrapper for QMessageBox Question yes/abort
      msg = QMessageBox()
      msg.setIcon(QMessageBox.Question)
      msg.setWindowTitle(Title)
      msg.setText(Text)       
      msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
      return msg.exec_()

    def MB_YesNo(self, Title, Text):
    # wrapper for QMessageBox Question yes/abort
      msg = QMessageBox()
      msg.setIcon(QMessageBox.Question)
      msg.setWindowTitle(Title)
      msg.setText(Text)       
      msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
      return msg.exec_()

    def MB_Info(self, Title, Text):
    # wrapper for QMessageBox Info
      msg = QMessageBox()
      msg.setIcon(QMessageBox.Information)
      msg.setWindowTitle(Title)
      msg.setText(Text)       
      msg.setStandardButtons(QMessageBox.Ok)
      return msg.exec_()

    def MB_Warning(self, Title, Text):
    # wrapper for QMessageBox Info
      msg = QMessageBox()
      msg.setIcon(QMessageBox.Warning)
      msg.setWindowTitle(Title)
      msg.setText(Text)       
      msg.setStandardButtons(QMessageBox.Ok)
      return msg.exec_()

    def init(self, Window, DAQconfFile, ConfDir=None, WDname=None):
 # initialisation 
      super().setupUi(Window) # initialize base class
      self.Window = Window

# set display options, fonts etc.
      self.setOptions()

# set help 
      self.setHelp_EN()

# find user home directory and create directory 'PhyPi' 
      self.homedir = os.getenv('HOME')
      if ConfDir != None:
        if ConfDir[0] != '/':    # relative path
          self.ConfDir = self.homedir + '/' + ConfDir
        else:                     # absolute path          
          self.ConfDir = ConfDir
      else:
        if self.ConfDir == None: self.ConfDir = self.homedir + '/picoCosmo' 
      if not os.path.exists(self.ConfDir): 
        os.makedirs(self.ConfDir)

# set initial working Directory
      if WDname != None:
        if WDname[0] != '/':    # relative path
          self.WDname = self.homedir + '/' + WDname          
        else:                   # absolute path
          self.WDname = WDname          
      else:
        if self.WDname == None: self.WDname = self.ConfDir
      if not os.path.exists(self.WDname): 
        os.makedirs(self.WDname)

      self.lE_WorkDir.setText(self.WDname)

# define actions
      self.pB_abort.clicked.connect(QtCore.QCoreApplication.instance().quit) 
      self.rB_EditMode.clicked.connect(self.actionEditConfig) 
      self.pB_StartRun.clicked.connect(self.actionStartRun) 
      self.pB_FileSelect.clicked.connect(self.selectConfigFile)
      self.pB_WDselect.clicked.connect(self.selectWD)
      self.pB_Help.clicked.connect(self.setHelp_EN)
      self.pB_Hilfe.clicked.connect(self.setHelp_DE)
      self.pB_SaveConfig.clicked.connect(self.saveDefaultConfig)
      self.pB_ReloadConfig.clicked.connect(self.reloadConfigs)
      self.pB_loadPSconf.clicked.connect(self.loadPSconf)
      self.pB_loadBMconf.clicked.connect(self.loadBMconf)
      self.pB_loadPFconf.clicked.connect(self.loadPFconf)

# initialization dependent on DAQ config file
      self.initDAQ(DAQconfFile)

    def readConfigs(self, DAQconfD):  
      # read configuration options from file names in DAQ config dictionary

      if "DeviceFile" in DAQconfD: 
        self.PSfile = DAQconfD["DeviceFile"] # configuration file for scope
      else:
        self.MB_Warning('Warning', 
                   'no device configuration file - exiting')
        print('     no device configuration file - exiting')
        exit(1)

      if "BMfile" in DAQconfD: 
        self.BMfile = DAQconfD["BMfile"] # Buffer Manager configuration file 
      else:
        self.MB_Warning('Warning', 
                   'no BM configuration file - exiting')
        print('     no BM configuration file - exiting')
        exit(1)

      if "PFfile" in DAQconfD: 
        self.PFfile = DAQconfD["PFfile"] # Buffer Manager configuration file 
      else:
        self.MB_Warning('Warning', 
                   'no pulse filter configuration file - exiting')
        print('     no pulse filter configuration file - exiting')
        exit(1)

   # display config data in GUI
      self.pTE_OsciConfig.setPlainText(open(self.iwd + '/' + self.PSfile, 'r').read() )
      self.pTE_BMconfig.setPlainText(open(self.iwd + '/' + self.BMfile, 'r').read() )
      self.pTE_PFconfig.setPlainText(open(self.iwd + '/' + self.PFfile, 'r').read() )

    def initDAQ(self, DAQconfFile):
      # initialize DAQ from config files - need absolute path
      path = os.path.dirname(DAQconfFile)
      if path == '': path = '.'
      self.iwd = path    # initial working direcotory 

      try:
        with open(DAQconfFile) as f:
          DAQconf = f.read()
          try:
            DAQconfdict=yaml.load(DAQconf, Loader=yaml.Loader)
          except Exception as e:
           # self.MB_Warning('Warning', 
           #   'DAQ configuration not valid yaml format ' + DAQconfFile +'\n' + str(e) ) 
           print('     DAQ configuration not valid yaml format' + DAQconfFile)
         # exit(1)
           return   
      except Exception as e:
        print('   DAQ configuration file ' + DAQconfFile + ' not found')
     # self.MB_Warning('Warning', 
     #     'failed to read DAQ configuration file ' + DAQconfFile +'\n' + str(e))
     # exit(1)
        DAQconf = "missing !"
        return
      
      self.lE_DAQConfFile.setText(DAQconfFile)
      RunTag = os.path.split(DAQconfFile)[1].split('.')[0]
      self.lE_RunTag.setText(RunTag)

      print('   - DAQ configuration from file ' + DAQconfFile)

      # display config in GUI
      self.pTE_DAQConfig.setPlainText(DAQconf)
      # read configurations from DAQ dictionary
      self.readConfigs(DAQconfdict)

# - end iniDAQ

    def reloadConfigs(self):
    # reload configs after edits in DAQconfig
      # retrieve actual configuration from GUI
      DAQconf = self.pTE_DAQConfig.toPlainText() 
      # check if valid yaml syntax
      try:
        DAQconfDict = yaml.load(DAQconf, Loader=yaml.Loader)
      except Exception as e:
        self.MB_Warning('Warning', 
          'DAQ config is not valid yaml format \n' + str(e) )       
        return 1
      self.readConfigs(DAQconfDict)

    def setOptions(self):
      # set font for plainTextEdit to monospace
      monofont = QtGui.QFont()
      monofont.setStyleHint(QtGui.QFont.TypeWriter)
      monofont.setFamily("unexistentfont")        
      self.pTE_OsciConfig.setFont(monofont)
      self.pTE_BMconfig.setFont(monofont)
      self.pTE_PFconfig.setFont(monofont)
      # no line wrap, horizontal scroll bar instead
      self.pTE_OsciConfig.setLineWrapMode(0)
      self.pTE_BMconfig.setLineWrapMode(0)
      self.pTE_PFconfig.setLineWrapMode(0)

    def setHelp_DE(self):
      self.TE_Help.setText(open('doc/Hilfe.html', 'r').read() ) 

    def setHelp_EN(self):
      self.TE_Help.setText(open('doc/help.html', 'r').read() )

    def loadPSconf(self):
      path2File = QtWidgets.QFileDialog.getOpenFileName(None,
       'Picoscope config', self.iwd, 'yaml(*.yaml)')
      FileName = str(path2File[0]).strip()
      if FileName != '' :
        # print('selected File ' + str(FileName) )
        self.pTE_OsciConfig.setPlainText(open(FileName, 'r').read() )

    def loadBMconf(self):
      path2File = QtWidgets.QFileDialog.getOpenFileName(None,
       'Buffer Manager config', self.iwd, 'yaml(*.yaml)')
      FileName = str(path2File[0]).strip()
      if FileName != '' :
        # print('selected File ' + str(FileName) )
        self.pTE_BMconfig.setPlainText(open(FileName, 'r').read() )

    def loadPFconf(self):
      path2File = QtWidgets.QFileDialog.getOpenFileName(None,
       'Pulse Filter config', self.iwd, 'yaml(*.yaml)')
      FileName = str(path2File[0]).strip()
      if FileName != '' :
        # print('selected File ' + str(FileName) )
        self.pTE_PFconfig.setPlainText(open(FileName, 'r').read() )

    def selectConfigFile(self):
      path2File = QtWidgets.QFileDialog.getOpenFileName(parent=None,
         caption='DAQ config', directory=self.WDname, filter='DAQ(*.daq)')
      FileName = str(path2File[0]).strip()
      if FileName != '' :
        # print('selected File ' + str(FileName) )
        self.initDAQ(FileName)

    def selectWD(self):
      path2WD = QtWidgets.QFileDialog.getExistingDirectory(None, '~')
      WDname = str(path2WD).strip()
      if WDname != '' :
        # print('selected Directory' + WDname )
         self.lE_WorkDir.setText(WDname)
         self.WDname = WDname

    def actionEditConfig(self):
        checked = self.rB_EditMode.isChecked()
        self.pTE_DAQConfig.setReadOnly(not checked)
        self.pB_ReloadConfig.setEnabled(checked)
        self.pTE_OsciConfig.setReadOnly(not checked)
        self.pTE_BMconfig.setReadOnly(not checked)
        self.pTE_PFconfig.setReadOnly(not checked)

    def saveConfigs(self, confdir, DAQfile=None, verbose=1):
      # save configuration in ~/picoCosmo
        
        # retrieve actual configuration from GUI
        DAQconf = self.pTE_DAQConfig.toPlainText() 
        # check if valid yaml syntax
        try:
          DAQconfDict = yaml.load(DAQconf, Loader=yaml.Loader)
        except Exception as e:
          self.MB_Warning('Warning', 
                          'DAQ config is not valid yaml format \n' + str(e) )       
          return 1

        PSconf = self.pTE_OsciConfig.toPlainText() 
        # check if valid yaml syntax
        try:
          d = yaml.load(PSconf, Loader=yaml.Loader)
        except Exception as e:
          self.MB_Warning('Warning', 
            'PicoScope config is not valid yaml format\n' + str(e) )       
          return 1

        BMconf = self.pTE_BMconfig.toPlainText() 
        try:
          d = yaml.load(BMconf, Loader=yaml.Loader)
        except Exception as e:
          self.MB_Warning('Warning', 
            'Buffer Manager config is not valid yaml format \n'+ str(e) )       
          return 1

        PFconf = self.pTE_PFconfig.toPlainText() 
        try:
          d = yaml.load(PFconf, Loader=yaml.Loader)
        except Exception as e:
          self.MB_Warning('Warning', 
            'Pulse Filter config is not valid yaml format \n' + str(e) )       
          return 1

        # generate file name from RunTag if not given explicitely
        if(DAQfile)==None:
          DAQfile= str(self.lE_RunTag.text() ).replace(' ','') + '.daq'
        fullDAQfile = confdir + '/' + DAQfile
        if verbose:
          if self.MB_Question('Question', 
            'saving Config to file ' + fullDAQfile) == QMessageBox.Cancel:
            return 1
        try:
          fDAQ = open(fullDAQfile, 'w')
          print(DAQconf, file = fDAQ )
          self.DAQfile = DAQfile
          fDAQ.close()     
        except Exception as e:
          self.MB_Warning('Warning', 
            'Failed to save '+ fullDAQfile + '\n' + str(e) )       
          return 1

        # store all other configuration files   

        fnam = DAQconfDict['DeviceFile']
        dir, fn = os.path.split(fnam)
        if dir != '': 
          if not os.path.exists(confdir + '/' + dir):
            os.makedirs(confdir + '/' + dir)
        try: 
          fPS = open(confdir + '/' + fnam, 'w')
          print(PSconf, file = fPS )
          fPS.close()
        except Exception as e:
          self.MB_Warning('Warning', 
            'Failed to store ' + fnam + ' in directory ' + confdir + '\n' + str(e) )       
          return 1

        fnam = DAQconfDict['BMfile']
        dir, fn = os.path.split(fnam)
        if dir != '': 
          if not os.path.exists(confdir + '/' + dir):
            os.makedirs(confdir + '/' + dir) 
        try:
          fBM = open(confdir + '/' + fnam, 'w')
          print(BMconf, file = fBM )
          fBM.close()
        except Exception as e:
          self.MB_Warning('Warning', 
            'Failed to store ' + fnam + ' in directory ' + confdir + '\n' + str(e) )       
          return 1

        fnam = DAQconfDict['PFfile']
        dir, fn = os.path.split(fnam)
        if dir != '': 
          if not os.path.exists(confdir + '/' + dir):
            os.makedirs(confdir + '/' + dir)
        try: 
          fPF = open(confdir + '/' + fnam, 'w')
          print(PFconf, file = fPF )
          fPF.close()
        except Exception as e:
          self.MB_Warning('Warning', 
            'Failed to store ' + fnam + ' in directory ' + confdir + '\n' + str(e) )       
          return 1

        return 0

    def saveDefaultConfig(self):
    # save configuration
      # propose name for DAQ configuration file from RunTag 
      _file = self.ConfDir + '/' + str(self.lE_RunTag.text() ).replace(' ','') + '.daq'
      # select file and direcotory 
      path2File = QtWidgets.QFileDialog.getSaveFileName(None,
         'save configuration as', _file, 'daq(*.daq)')
      fullDAQfile = str(path2File[0]).strip()
      if  fullDAQfile != '' :
      # remember new config directory
        self.ConfDir = os.path.dirname(fullDAQfile)
        DAQfile = os.path.basename(fullDAQfile)
      else:
        print("   abort - no file name given") 
        return 1
     # set name and save all configs
      self.lE_DAQConfFile.setText(fullDAQfile)
      if self.saveConfigs(self.ConfDir, DAQfile=DAQfile, verbose=0):
        print("   !!! failed to save configuration files")
        return 1
      else:
        print("   - configuration files stored in directory " + self.ConfDir)
        return 0 

    def saveEnvironment(self):
      '''
        Save picoCosmo configuration to file ~/CONFIG_ENVIRONMENT_file
      '''
      hd = os.getenv('HOME')
  # ... and find name of work directory
      cfgname = hd + '/' + CONFIG_ENVIRONMENT_file
      fcfg = open(cfgname, 'w')
      print('work_directory: ', self.WDname, file=fcfg)
      print('config_directory: ', self.ConfDir, file=fcfg)
      print('daq_file: ', os.path.basename(self.DAQfile), file=fcfg)
 
    def actionStartRun(self):
      # start script runCosmo.py in subdirectory

      # time stamp for this run
      datetime=time.strftime('%y%m%d-%H%M', time.localtime())
      RunTag = str(self.lE_RunTag.text() ).replace(' ','')

      # generate config files for new run in dedicated subdirectory
      self.runDir = (RunTag + '_' + datetime)
      self.path_to_WD = self.WDname + '/' + self.runDir
      if not os.path.exists(self.path_to_WD): 
        os.makedirs(self.path_to_WD)
      # store config in working directory 
      if self.saveConfigs(self.path_to_WD):
          print("   !!! failed to save Config files for this run") 
          return 
      else: 
          print("   - files for this run stored in directory " + self.path_to_WD) 

      # save changes to picocosmo environment
      self.saveEnvironment()

    # close GUI window and start runCosmo 
      print('\n*==* CosmoGui: closing window and starting runCosmo.py')
#      self.Window.close()
      self.Window.hide()

      # start runCosmo
      self.start_runCosmo()

    # exit or continue ? 
      if self.MB_YesNo('End Dialog','Exit picoCosmo ? ') == QMessageBox.Yes:
        QtCore.QCoreApplication.instance().quit()
        print('*==* picoCosmo: exit \n')
      else:
        self.Window.show()
              
    def start_runCosmo(self):
        CosmoDir = os.getcwd()
        subprocess.run(args =
            [sys.executable, '-m', 'picocosmo.runCosmoDAQ', self.DAQfile],
                      cwd = self.WDname + '/' + self.runDir)
        
# - end Class Ui_CosmoWindow

def runCosmoUi():
    
  script = sys.argv[0]
  print('\n*==* ' + script + ' running \n')

  # get relevant paths
  path_to_picoCosmo = os.path.dirname(script)
  if path_to_picoCosmo=='': path_to_picoCosmo='./' # python2 work-around
  homedir = os.getenv('HOME')
  # ... and find name of work directory
  cfgname = homedir + '/' + CONFIG_ENVIRONMENT_file
  try:
    with  open(cfgname) as cfg: 
      cfg_dict = yaml.load(cfg, Loader=yaml.Loader)
  except:
    cfgname = path_to_picoCosmo + '/' + CONFIG_ENVIRONMENT_file
    try:
      with  open(cfgname) as cfg: 
        cfg_dict=yaml.load(cfg, Loader=yaml.Loader)      
    except:
      print(2*' ', ' !!! no valid file '
        +  CONFIG_ENVIRONMENT_file + ' found - using defaults')
      cfg_dict = {}
      cfg_dict['work_directory'] = '~'
      cfg_dict['config_directory'] = ' '
      cfg_dict['daq_file'] = 'default.daq'
      
  work_directory = cfg_dict['work_directory']
  if work_directory == '~':  work_directory = homedir
  if work_directory == '.':  work_directory = os.getcwd()
  conf_directory = cfg_dict['config_directory']
  if conf_directory == '~':  conf_directory = homedir
  if conf_directory == '.':  conf_directory = os.getcwd()
  if conf_directory == ' ':  conf_directory = path_to_picoCosmo
  DAQconfFile = conf_directory + '/' + cfg_dict['daq_file']
  
# check for / read command line arguments
  # get DAQ configuration file
  if len(sys.argv)==2:
    if sys.argv[1] != '':
      DAQconfFile = os.path.abspath(sys.argv[1]) # with full path to file
      print (DAQconfFile)

# start GUI
  if path_to_picoCosmo!= '':
    os.chdir(path_to_picoCosmo) # change path to where CosmoGui lives
  app = QtWidgets.QApplication(sys.argv)
  MainWindow = QtWidgets.QMainWindow()
  ui = ComoGuiUiInterface()
  ui.init(MainWindow, DAQconfFile,
           ConfDir=conf_directory, WDname = work_directory)

# start pyqt event loop
  MainWindow.show()
  sys.exit(app.exec_())

if __name__ == "__main__": # - - - - - - - - - - - - - - - - - - - -
  runCosmoUi()
