"""
     CCP4JobControlGui.py: CCP4 GUI Project
     Copyright (C) 2016STFC

     This library is free software: you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public License
     version 3, modified in accordance with the provisions of the 
     license to address the requirements of UK law.sstac
 
     You should have received a copy of the modified GNU Lesser General 
     Public License along with this library.  If not, copies may be 
     downloaded from http://www.ccp4.ac.uk/ccp4license.php
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU Lesser General Public License for more details.
"""

"""
   Liz Potterton april 2016 - Gui for remote running
"""

import os, glob, time, functools
from PyQt4 import QtGui,QtCore
from CCP4ErrorHandling import *
import CCP4Utils,CCP4Modules,CCP4Annotation,CCP4Widgets
import CCP4Container,CCP4JobServer

class CServerParamsDialog(QtGui.QDialog):
    insts = None
    guiDef = { 'machine' : [ 'Server' , 'machine', 'Choose a server'],
               'username':  [ 'Username on server' ,  'Your username on server'],
               'password' :  [ 'Password on server','Your password on server' ],
               'keyFilename' : [ 'SSH key filename', 'Local file containing SSH public keys' ],
               'ccp4Dir' :  [ 'CCP4 directory on server', 'Full path of CCP4 on the server machine' ],
               'tempDir' :  [ 'Work directory on server', 'Full path of temporary work directory server machine' ],
               'sge_root' :  [ 'SGE_ROOT (Sun gridengine dir)', 'Full path of Sun grid engine root directory server machine' ],
               'queueOptionsFile' : [ 'Queue option file', 'File containing options for the job queue' ],
               'timeout' :  [ 'Timeout in seconds', 'Wait for connection' ]
             }
      
    def userGuiOptions(self,mechanism,validate=None,customCodeFile=None):
      if mechanism in ['ssh','ssh_shared','qsub_remote','qsub_shared']:
        if validate is None or validate == 'password':
          ret = ['username','password','machine']
        elif validate == 'key_filename':
          ret = ['username','key_filename','machine']
        elif validate == 'pass_key_filename':
          ret = ['username','key_filename','password','machine']
        if mechanism in ['ssh','qsub_remote']: ret.append('tempDir')
        #if mechanism in ['qsub_shared','qsub_remote']: ret.append('sge_root')
      elif mechanism == 'qsub_local':
        ret = []
      elif mechanism == 'custom':
        if customCodeFile is not None:
          return CCP4Modules.JOBCONTROLLER().customHandler(customCodeFile=customCodeFile).SHOW_USER_PARAMS
        else:
          return ['username','password']
      if mechanism in ['qsub_local','qsub_remote','qsub_shared']:
          ret.append('queueOptionsFile')
      return ret

  
    def getParams(self):
      p = CCP4JobServer.CServerParams(machine = self.get('machine'),
                                             username = self.get('username'),
                                             password = self.get('password'),
                                             keyFilename = self.get('keyFilename'),
                                             mechanism = self.get('mechanism'),
                                             validate= self.get('validate'),
                                             ccp4Dir = self.get('ccp4Dir'),
                                             tempDir = self.get('tempDir'),
                                             sge_root = self.get('sge_root'),
                                             customCodeFile = self.get('customCodeFile'),
                                             queueOptionsFile = self.get('queueOptionsFile'))
      print 'CServerParamsDialog.getParams',p
      return p
                                                 

    def get(self,param):
      if not self.widgets.has_key(param):
        serverGroup = self.container.get('SERVERGROUP'+str(self.modeGroup.checkedId()))
        if param == 'machine':
          return str(getattr(serverGroup,'serverList')[0])
        elif param == 'timeout':
          if serverGroup.timeout.isSet():
            return float(serverGroup.timeout)
          else:
            return None
        elif param not in ['username','password']:
          return str(getattr(serverGroup,param))
        else:
          return ''
      
      if param in ['queueOptionsFile']:
        self.widgets['queueOptionsFile'].updateModelFromView()
        return str(self.queueOptionsFile)
      else:
        if isinstance(self.widgets[param],QtGui.QLineEdit):
          if param == 'timeout':
            if len(self.widgets[param].text()) == 0:
              return None
            else:
              return float(self.widgets[param].text())
          return str(self.widgets[param].text())
        else:
          return str(self.widgets[param].currentText())

    def setInfo(self,info=None):
      if info is None:
        self.widgets['info'].setText('')
      else:
        self.widgets['info'].setText(info)

    def  valid(self):
      return True
   
    def __init__(self,parent=None,params={}):
        QtGui.QDialog.__init__(self,parent)
        import CCP4Utils
        try:
          self.loadConfig()
        except:
          print 'Failed loading server config file from CCP4I2/local_setup/servers_config.params.xml'
        
        self.setWindowTitle('Choose server to run job')
        layout = QtGui.QVBoxLayout()
        self.setLayout(layout)
        self.setMinimumWidth(500)


        frame = QtGui.QFrame(self)
        frame.setLayout(QtGui.QVBoxLayout())
        frame.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain)
        MARGIN = 6
        frame.layout().setSpacing(MARGIN)
        frame.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN)
        frame.layout().addWidget(CCP4Widgets.CItalicLabel('Choose group of servers to use..',self))
        self.layout().addWidget(frame)
        
        self.modeGroup = QtGui.QButtonGroup(self)
        default = 0
        row = 0
        for idx in range(1,10):
          group = self.container.get('SERVERGROUP'+str(idx))
          #if group is not None: print 'SERVERGROUP',idx,group, group.isSet(), len(group.serverList)
          if group is not None and len(group.serverList)>0:
            but = QtGui.QRadioButton(str(group.name),self)
            self.modeGroup.addButton(but,idx)
            frame.layout().addWidget(but)
            row = row + 1
            if default==0: default = idx
        #print 'default modeGroup',default
        try:            
          self.modeGroup.button(default).setChecked(True)
        except:
          print 'Failed to set modeGroup',default

        self.customFrame = QtGui.QFrame(self)
        self.customFrame.setLayout(QtGui.QVBoxLayout())
        self.layout().addWidget(self.customFrame)

        butBox = QtGui.QDialogButtonBox(self)
        but = butBox.addButton("Submit job",QtGui.QDialogButtonBox.AcceptRole)
        but.clicked.connect(self.accept)
        but = butBox.addButton("Help",QtGui.QDialogButtonBox.HelpRole)
        but.clicked.connect(self.help)
        but = butBox.addButton("Cancel",QtGui.QDialogButtonBox.RejectRole)
        but.clicked.connect(self.reject)       
        self.layout().addWidget(butBox)

        self.connect(self.modeGroup,QtCore.SIGNAL('buttonReleased(int)'),self.loadServers)
        self.loadServers()
        
    def drawDetails(self,serverGroup,params={}):
        self.widgets = { }
        self.labels = { }
        #params['ccp4Dir'] = params.get('ccp4Dir',CCP4Utils.getCCP4Dir())
        #params['username'] = params.get('username',CCP4Utils.getUserId())
        
        self.detailsFrame = QtGui.QFrame(self)
        layout = QtGui.QGridLayout()
        self.detailsFrame.setLayout(layout)
        self.detailsFrame.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Plain)
        layout.addWidget(CCP4Widgets.CItalicLabel('Details',self),0,0,1,2)
        row = 1
          
        #for param in ['machine', 'ccp4Dir', 'username', 'password','keyFilename','queueOptionsFile']:
        #for param in CServerParamsDialog.guiDef.keys():
        for param in self.userGuiOptions(mechanism=str(serverGroup.mechanism),validate=str(serverGroup.validate),
                                     customCodeFile=str(serverGroup.customCodeFile)):

          self.labels[param] = QtGui.QLabel(CServerParamsDialog.guiDef[param][0],self)
          layout.addWidget(self.labels[param] ,row,0)
          if param == 'machine':
            self.widgets[param] = QtGui.QComboBox(self)
          elif param == 'queueOptionsFile':
            import CCP4File
            self.queueOptionsFile = CCP4File.CDataFile(parent=self)
            self.widgets[param] = CCP4Widgets.CDataFileView(self,model=self.queueOptionsFile)
          else:
            self.widgets[param] = QtGui.QLineEdit(self)
            self.widgets[param].setText(params.get(param,''))
          self.widgets[param].setToolTip(CServerParamsDialog.guiDef[param][1])
          if param == 'password' : self.widgets[param].setEchoMode(QtGui.QLineEdit.Password)
          layout.addWidget(self.widgets[param],row,1)
          row = row + 1

        if serverGroup.mechanism == 'custom':
          for i in range(self.customFrame.layout().count()):
            try:
              w = self.customFrame.layout().takeAt(0).widget()
              w.hide()
              w.deleteLater()
            except:
              pass
          guiFunction = getattr(CCP4Modules.JOBCONTROLLER().customHandler(customCodeFile=serverGroup.customCodeFile),'guiFrame',None)
          if guiFunction is not None and callable(guiFunction):
            try:
              guiFunction(parentFrame=self.customFrame)
            except Exception as e:
              print 'Failed drawing custom widgets',str(e)
          self.customFrame.show()
        else:
          self.customFrame.hide()


    def loadServers(self,mode=None):
      serverGroup = self.container.get('SERVERGROUP'+str(self.modeGroup.checkedId()))
      if serverGroup is None:
        warningMess = QtGui.QMessageBox.warning(self,'Run on server','''To run on server you must first configure servers.
See under Utilities -> System administrator tools''')
        self.hide()
        return
        
      if hasattr(self,'detailsFrame'):
        idx = self.layout().indexOf(self.detailsFrame)
        self.layout().takeAt(idx)
        self.detailsFrame.hide()
        self.detailsFrame.deleteLater()
      else:
        idx = 1
      self.drawDetails(serverGroup)
      self.layout().insertWidget(idx,self.detailsFrame)
      
      #print 'loadServers',self.modeGroup.checkedId(),serverGroup,len(serverGroup.serverList)
      if self.widgets.has_key('machine'):
        self.widgets['machine'].setEditable(bool(serverGroup.userExtensible))
        self.widgets['machine'].clear()
        for item in serverGroup.serverList:
          self.widgets['machine'].addItem(str(item))

      for key in ['ccp4Dir','keyFilename','tempDir']:
        if self.widgets.has_key(key):
          if getattr(serverGroup,key).isSet():
            self.widgets[key].setText(str(getattr(serverGroup,key)))
          else:
            self.widgets[key].setText('')
            
      if self.widgets.has_key('password'):
        if serverGroup.validate is None or str(serverGroup.validate) == 'password':
          self.labels['password'].setText('Your password on server')
          self.widgets['password'].setToolTip('Your password on server')
        elif str(serverGroup.validate) == 'key_filename':
          pass
        elif str(serverGroup.validate) == 'pass_key_filename':
          self.labels['password'].setText('Your key file password')
          self.widgets['password'].setToolTip('Your key file password')

        
      
    def loadConfig(self):
      import CCP4Modules
      self.container = CCP4Modules.SERVERSETUP()
          
    def accept(self):
        QtGui.QDialog.accept(self)

    def reject(self):
        QtGui.QDialog.reject(self)

    def setInfo(self,text):
      if not self.widgets.has_key('info'):
         self.widgets['info'] = QtGui.QLabel(self)
         self.widgets['info'].setObjectName('warning')
         self.layout().insertWidget(0,self.widgets['info'])
      if text is None:
        self.widgets['info'].setText('')
      else:
        self.widgets['info'].setText(text)

            
    def help(self):
      CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='servers.html',newTab=True)

class CServerGroupView(CCP4Widgets.CComplexLineWidget):
    
  MODEL_CLASS = CCP4Annotation.CServerGroup
  
  def __init__(self,parent=None,model=None,qualifiers={}):
    qualis = { 'vboxLayout' : True }
    qualis.update(qualifiers)
    CCP4Widgets.CComplexLineWidget.__init__(self,parent=parent,qualifiers=qualis)
    self.layout().takeAt(0).widget().deleteLater()
    self.frames = {}
    self.widgets['name'] = CCP4Widgets.CStringView(self)
    qualis 
    self.widgets['mechanism'] = CCP4Widgets.CStringView( self,qualifiers=model.mechanism.qualifiers() )
    self.widgets['validate'] = CCP4Widgets.CRadioButtonGroup(self)
    self.widgets['validate'].addRadioButton('password','by password')
    self.widgets['validate'].addRadioButton('key_filename','by key file')
    self.widgets['validate'].addRadioButton('pass_key_filename','by passworded key file')
    self.connect(self.widgets['mechanism'].widget,QtCore.SIGNAL('currentIndexChanged(int)'),self.changeMechanism)
    self.connect(self.widgets['validate'],QtCore.SIGNAL('buttonReleased(int)'),self.changeValidate)
    '''
    if model is not None:
      self.widgets['mechanism'].populate(menuItems=model.mechanism.qualifiers('enumerators'),menuText=model.mechanism.qualifiers('menuText'))
    '''
    self.widgets['serverList'] = CCP4Widgets.CListView(self, qualifiers= { 'editorClassName' : 'CStringView' })
    self.widgets['userExtensible'] = CCP4Widgets.CBooleanView(self)
    self.widgets['ccp4Dir'] = CCP4Widgets.CStringView(self,qualifiers={  'allowUndefined' : True } )
    
    self.widgets['tempDir'] = CCP4Widgets.CStringView(self,qualifiers={  'allowUndefined' : True } )
    self.widgets['sge_root'] = CCP4Widgets.CStringView(self,qualifiers={  'allowUndefined' : True } )
    self.widgets['keyFilename'] = CCP4Widgets.CStringView(self,qualifiers={  'allowUndefined' : True } )
    self.widgets['timeout'] = CCP4Widgets.CFloatView(self,qualifiers={  'allowUndefined' : True } )
    self.widgets['maxTries'] = CCP4Widgets.CIntView(self,qualifiers={  'allowUndefined' : False } )
    self.widgets['customCodeFile'] = CCP4Widgets.CDataFileView(self,qualifiers={  'allowUndefined' : True , 'jobCombo' : False, 'browseDb': False } )
    
    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Name for server group',self))
    line.addWidget(self.widgets['name'])
    line.addWidget(QtGui.QLabel('using mechanism',self))
    line.addWidget(self.widgets['mechanism'])
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(QtGui.QLabel('Validate by',self))
    for item in ['password','key_filename','pass_key_filename']:
        line.addWidget(self.widgets['validate'].getButton(item))
    line.addStretch(2)
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    self.widgets['serverList'].setLineWidth(1)
    self.widgets['serverList'].setFrameStyle(QtGui.QFrame.Box|QtGui.QFrame.Plain)
    line.addWidget(self.widgets['serverList'])
    self.layout().addLayout(line)
    self.widgets['serverList'].setListVisible(True)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.widgets['userExtensible'])
    line.addWidget(QtGui.QLabel('Allow users to add alternative servers at run time',self))
    line.addStretch(2)
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(CCP4Widgets.CItalicLabel('CCP4 distro directory on these servers',self))
    self.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.widgets['ccp4Dir'])
    self.layout().addLayout(line)
    
    frame = self.makeFrame('tempDir')
    line = QtGui.QHBoxLayout()
    line.addWidget(CCP4Widgets.CItalicLabel('Temporary directory (eg /tmp/$USER)',self))
    frame.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.widgets['tempDir'])
    frame.layout().addLayout(line)

    frame = self.makeFrame('sge_root')
    line = QtGui.QHBoxLayout()
    line.addWidget(CCP4Widgets.CItalicLabel('SGE_ROOT (Sun Grid Engine dir)',self))
    frame.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.widgets['sge_root'])
    frame.layout().addLayout(line)
   
    frame = self.makeFrame('key_filename')
    frame.layout().addWidget(CCP4Widgets.CItalicLabel('Local SSH key filename (eg $HOME/.ssh/id_rsa)',self))
    frame.layout().addWidget(self.widgets['keyFilename'])

    line = self.makeFrame('timeout',vertical=False)
    line.layout().addWidget(CCP4Widgets.CItalicLabel('Timeout (seconds)',self))
    line.layout().addWidget(self.widgets['timeout'])
    line.layout().addWidget(CCP4Widgets.CItalicLabel('Maximum tries',self))
    line.layout().addWidget(self.widgets['maxTries'])
    self.layout().addWidget(line)

    frame = self.makeFrame('customCodeFile')
    line = QtGui.QHBoxLayout()
    line.addWidget(CCP4Widgets.CItalicLabel('Custom server code file',self))
    frame.layout().addLayout(line)
    line = QtGui.QHBoxLayout()
    line.addWidget(self.widgets['customCodeFile'])
    frame.layout().addLayout(line)
    
    line = QtGui.QHBoxLayout()
    self.testButton = QtGui.QToolButton(self)
    self.testButton.setText('Test server group')
    self.deleteButton = QtGui.QToolButton(self)
    self.deleteButton.setText('Delete server group')
    line.addStretch(5)
    line.addWidget(self.testButton )
    line.addWidget(self.deleteButton )
    self.layout().addLayout(line)

    self.setModel(model=model)
    self.updateViewFromModel()
    self.widgets['serverList'].updateViewFromModel()
    self.widgets['serverList'].handleRowChange(0,force=True)
    self.widgets['mechanism'].updateViewFromModel()
    self.changeMechanism(0)

  def makeFrame(self,name,vertical=True):
    self.frames[name] = QtGui.QFrame()
    if vertical:
      self.frames[name].setLayout(QtGui.QVBoxLayout())
    else:
      self.frames[name].setLayout(QtGui.QHBoxLayout())
    self.frames[name].layout().setSpacing(1)
    self.frames[name].layout().setContentsMargins(1,1,1,1)
    self.layout().addWidget(self.frames[name])
    return self.frames[name]
  

  def updateViewFromModel(self):
    # Exclude the serverList which is behaving oddly because
    # dataChanged signal from serverList object is relayed by
    # CServerGroup and causes this method to be invoked
    for item in ['name','userExtensible','ccp4Dir','tempDir','keyFilename','customCodeFile','timeout','maxTries']:
        self.widgets[item].updateViewFromModel()
    self.widgets['validate'].setValue(str(self.model.validate))
    self.changeValidate(self.model.validate.qualifiers('enumerators').index(str(self.model.validate)))

  def updateModelFromView(self):
    for item in ['name','userExtensible','ccp4Dir','tempDir','keyFilename','serverList','customCodeFile','timeout','maxTries']:
        self.widgets[item].updateModelFromView()
    self.model.validate.set(self.widgets['validate'].getValue())
    #print 'CServerGroupView.updateModelFromView',self.model.validate

  def changeMechanism(self,indx):
    mechanism = self.widgets['mechanism'].getWidgetText()
    #print 'CServerGroupView.changeMechanism',indx,mechanism
    if mechanism in ['custom']:
      self.frames['customCodeFile'].show()
    else:
      self.frames['customCodeFile'].hide()
    if mechanism in ['ssh','ssh_shared','qsub_remote','qsub_shared']:
      self.frames['timeout'].show()
    else:
      self.frames['timeout'].hide()
    if mechanism in ['ssh','qsub_remote','custom']:
      self.frames['tempDir'].show()
    else:
      self.frames['tempDir'].hide()
    if mechanism in ['qsub_local','qsub_remote','qsub_shared']:
      self.frames['sge_root'].show()
    else:
      self.frames['sge_root'].hide()
    
  def changeValidate(self,id):
    #print 'changeValidate',id
    self.model.validate.set(self.widgets['validate'].getValue())
    if id in [0]:
      self.frames['key_filename'].hide()
    else:
      self.frames['key_filename'].show()
    

class CServerSetupWindow(QtGui.QDialog):
  ''' This is the glue to get display the CTaskServerSetup as an independent window '''

  def __init__(self,parent):
    QtGui.QDialog.__init__(self,parent)
    self.setWindowTitle('Configure servers for remote running')
    #self.setMaximumWidth(650)
    #self.setMinimumHeight(500)
    self.setLayout(QtGui.QVBoxLayout())
    MARGIN = 2
    self.layout().setSpacing(MARGIN)
    self.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN)
    self.widgets= {}

    
    import CCP4Modules,CCP4Widgets
    self.container = CCP4Modules.SERVERSETUP()
    self.container.load()
    line = QtGui.QHBoxLayout()
    self.sourceLabel = QtGui.QLabel('Server setup loaded from ',self)
    line.addWidget(self.sourceLabel)
    self.layout().addLayout(line)
    self.altSourceBut = QtGui.QPushButton('Load alternate setup',self)
    line.addWidget(self.altSourceBut)
    self.connect(self.altSourceBut,QtCore.SIGNAL('clicked()'),self.handleLoadAlt)                                                  

    self.scrollArea= QtGui.QScrollArea(self)
    self.scrollArea.setMaximumWidth(570)
    self.scrollArea.setMinimumWidth(570)
    self.scrollArea.setMinimumHeight(500)
    self.layout().addWidget(self.scrollArea)
    self.contentFrame = QtGui.QFrame(self)
    self.scrollArea.setWidget(self.contentFrame)
    self.scrollArea.setWidgetResizable(1)
    self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
    self.contentLayout = QtGui.QVBoxLayout()
    self.contentLayout.setSpacing(MARGIN)
    self.contentLayout.setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN)
   
    self.contentFrame.setLayout(self.contentLayout)

    self.makeAllWidgets()

    line = QtGui.QHBoxLayout()
    self.layout().addLayout(line)
    addButton = QtGui.QToolButton(self)
    addButton.setText('Add another server group')
    line.addStretch(4)
    line.addWidget(addButton)
    self.layout().addLayout(line)
    self.connect(addButton,QtCore.SIGNAL('clicked()'),self.handleAdd)

    self.buttons = QtGui.QDialogButtonBox(self)
    but = self.buttons.addButton(QtGui.QDialogButtonBox.Help)
    self.connect(but,QtCore.SIGNAL('clicked()'),self.help)
    if self.container.writeAccess('installation'):
      but = self.buttons.addButton('Save for CCP4 installation',QtGui.QDialogButtonBox.ApplyRole)
      self.connect(but,QtCore.SIGNAL('clicked()'),functools.partial(self.doApply,'installation'))
    but = self.buttons.addButton('Save for me only',QtGui.QDialogButtonBox.ApplyRole)
    self.connect(but,QtCore.SIGNAL('clicked()'),functools.partial(self.doApply,'user'))
    but = self.buttons.addButton(QtGui.QDialogButtonBox.Close)
    self.connect(but,QtCore.SIGNAL('clicked()'),self.close)
    line = QtGui.QHBoxLayout()
    line.addStretch(0.2)
    line.addWidget(self.buttons)
    line.addStretch(0.2)
    self.layout().addLayout(line)

  def makeAllWidgets(self):
    #print 'makeAllWidgets',self.container,self.container._value,self.getNServerGroups()
    n = 1
    while self.container.get('SERVERGROUP'+str(n)) is not None:
      self.makeWidget(n)
      n = n + 1

  def clear(self):
    for key,w in self.widgets.items():
      w.hide()
      w.deleteLater()
    self.widgets = {}

  def makeWidget(self,n):
    self.widgets['SERVERGROUP'+str(n)] = CServerGroupView(self,self.container.get('SERVERGROUP'+str(n)))
    self.widgets['SERVERGROUP'+str(n)].setMaximumWidth(550)
    self.widgets['SERVERGROUP'+str(n)].setMinimumWidth(550)
    self.connect(self.widgets['SERVERGROUP'+str(n)].deleteButton,QtCore.SIGNAL('clicked()'),functools.partial(self.handleDelete,n))
    self.connect(self.widgets['SERVERGROUP'+str(n)].testButton,QtCore.SIGNAL('clicked()'),functools.partial(self.handleTest,n))
    #self.layout().addWidget(self.widgets['SERVERGROUP'+str(n)])
    '''
    self.widgets['SERVERGROUP'+str(n)].updateViewFromModel()
    self.widgets['SERVERGROUP'+str(n)].widgets['serverList'].updateViewFromModel()
    self.widgets['SERVERGROUP'+str(n)].widgets['serverList'].handleRowChange(0,force=True)
    '''

    self.contentLayout.addWidget(self.widgets['SERVERGROUP'+str(n)])

  def show(self):
    self.setAltSourceButton()
    QtGui.QDialog.show(self)
    
  def setAltSourceButton(self):
    if self.container.source is not None:
      self.sourceLabel.setText('Server setup loaded from '+str(self.container.source))
      altSource =  ['user','installation'][1 -  ['user','installation'].index( self.container.source )]
      if os.path.exists(self.container.preferencesFile(altSource)[0]):
        self.altSourceBut.setText('Load '+altSource+' setup')
        self.altSourceBut.setEnabled(True)
        return
    self.altSourceBut.setText('Load alternate setup')
    self.altSourceBut.setEnabled(False)
    
  def handleLoadAlt(self):
    altSource =  ['user','installation'][1 -  ['user','installation'].index( self.container.source )]
    #print 'handleAltSource',altSource
    self.clear()
    self.container.clear()
    self.container.load(altSource)
    self.makeAllWidgets()
    self.setAltSourceButton()

  def getNServerGroups(self):
    n = 0
    while self.container.get('SERVERGROUP'+str(n+1)) is not None:
      n = n + 1
    return n

  def handleAdd(self):
    n = self.getNServerGroups() + 1
    self.container.setContents( { 'SERVERGROUP'+str(n) : { 'class' :CCP4Annotation.CServerGroup }} )  
    self.container.get('SERVERGROUP'+str(n)).setObjectName('SERVERGROUP'+str(n))
    self.makeWidget(n)
                                
    
  def handleDelete(self,n):
    #print 'CServerSetupWindow.handleDelete',n,self.getNServerGroups()
    nTot = self.getNServerGroups()
    if nTot==1: return
    self.widgets['SERVERGROUP'+str(n)].hide()
    self.widgets['SERVERGROUP'+str(n)].deleteLater()
    del self.widgets['SERVERGROUP'+str(n)]
    self.container.get('SERVERGROUP'+str(n)).blockSignals(True)
    self.container.deleteObject('SERVERGROUP'+str(n))

    if n < nTot:
      for ii in range(n,nTot):
        self.widgets['SERVERGROUP'+str(ii+1)].setObjectName('SERVERGROUP'+str(ii))
        self.widgets['SERVERGROUP'+str(ii)] = self.widgets.pop('SERVERGROUP'+str(ii+1))
        self.container.renameObject('SERVERGROUP'+str(ii+1),'SERVERGROUP'+str(ii))
        
    

  def handleTest(self,indx):
    if hasattr(self,'testDialog'):
      self.testDialog.hide()
      self.testDialog.deleteLater()
    self.testDialog = QtGui.QDialog(self)
    self.testDialog.setWindowTitle('Test remote server')
    self.testDialog.setLayout(QtGui.QVBoxLayout())
    self.testDialog.layout().addWidget(QtGui.QLabel('Test existance of CCP4 directory and temporary directory on each machine',self))
    line = QtGui.QHBoxLayout()
    self.testDialog.layout().addLayout(line)
    self.testDialogUsername = QtGui.QLineEdit(self.testDialog)
    line.addWidget(QtGui.QLabel('Test with username',self.testDialog))
    line.addWidget(self.testDialogUsername)
    line = QtGui.QHBoxLayout()
    self.testDialog.layout().addLayout(line)
    self.testDialogPassword = QtGui.QLineEdit(self.testDialog)
    self.testDialogPassword.setEchoMode(QtGui.QLineEdit.Password)
    line.addWidget(QtGui.QLabel('Test with password',self.testDialog))
    line.addWidget(self.testDialogPassword)
    buttons = QtGui.QDialogButtonBox(self.testDialog)
    but = buttons.addButton(QtGui.QDialogButtonBox.Cancel)
    self.connect(but,QtCore.SIGNAL('clicked()'),self.testDialog.close)
    but = buttons.addButton(QtGui.QDialogButtonBox.Apply)
    self.connect(but,QtCore.SIGNAL('clicked()'),functools.partial(self.showTests,indx))
    line = QtGui.QHBoxLayout()
    line.addStretch(0.2)
    line.addWidget(buttons)
    line.addStretch(0.2)
    self.testDialog.layout().addLayout(line)
    self.testDialog.show()
    self.testDialog.raise_()
                                       
    
  def showTests(self,indx):
    self.testDialog.close()
    if hasattr(self,'testMessage'):
      self.testMessage.hide()
      self.testMessage.deleteLater()
    else:
      self.connect(self,QtCore.SIGNAL('testMessage'),self.updateTestReport)
    message = ''
    self.testMessage = QtGui.QDialog(self)
    self.testMessage.setWindowTitle('Testing remote server')
    self.testMessage.setLayout(QtGui.QVBoxLayout())
    self.testMessage.layout().addWidget(QtGui.QLabel('Test existance of CCP4 directory and temp directory',self.testMessage))
    self.testReport = QtGui.QTextEdit(self.testDialog)
    self.testMessage.layout().addWidget(self.testReport)
    buttons = QtGui.QDialogButtonBox(self.testDialog)
    but = buttons.addButton(QtGui.QDialogButtonBox.Cancel)
    self.testMessage.layout().addWidget(buttons)
    self.connect(but,QtCore.SIGNAL('clicked()'),functools.partial(self.cancelTests,indx,True))
    self.testMessage.show()
    self.testMessage.raise_()

    self.connect(CCP4Modules.JOBCONTROLLER(),QtCore.SIGNAL('testMessage'),self.updateTestReport)
    import UtilityThread
    if not hasattr(self,'testThreads'): self.testThreads = {}
    self.testThreads[indx] = UtilityThread.UtilityThread(functools.partial(self.runTests,indx))
    self.testThreads[indx].finished.connect(functools.partial(self.cancelTests,indx))
    self.testThreads[indx].start()
    #self.runTests(indx)

  def updateTestReport(self,message):
      self.testReport.setReadOnly(False)
      mess=self.testReport.toPlainText() + '\n' + message
      self.testReport.setPlainText(mess)
      self.testReport.setReadOnly(True)
      self.testReport.repaint()
      
  def cancelTests(self,indx,force=False):
    if getattr(self,'testThreads',None) is not None and len(self.testThreads)>indx: 
      if force: self.testThreads[indx].exit()
      del self.testThreads[indx]
    if force: self.testMessage.close()

  def runTests(self,indx):
    sG = self.widgets['SERVERGROUP'+str(indx)]
    remoteFileList=[sG.widgets['ccp4Dir'].model.__str__()]
    if sG.widgets['mechanism'].model.__str__() in ['ssh','qsub_remote']:
      remoteFileList.append(sG.widgets['tempDir'].model.__str__())
    if sG.widgets['timeout'].model.isSet():
      timeout = float(sG.widgets['timeout'].model)
    else:
      timeout = None
    maxTries = int(sG.widgets['maxTries'].model)
    for server in sG.widgets['serverList'].model:
      #print 'testing server',server
      self.emit(QtCore.SIGNAL('testMessage'), 'Testing '+str(server)+'\n')
      try:
        ret = CCP4Modules.JOBCONTROLLER().testRemoteFiles(machine=str(server),username=str(self.testDialogUsername.text()),
           password = str(self.testDialogPassword.text()), remoteFileList=remoteFileList,timeout=timeout,maxTries=maxTries)
        message = ''
        labelList = ['CCP4 distro','Tmp dir']
        for ii in range(len(ret)):
          message =  message + labelList[ii] + ': ' + str(ret[ii]) +'\n'
      except CException as e:
        message =  '  Failed connection\n   ' + e.description()[0] + '\n   ' + str(e._reports[0]['details']) +'\n'
      except Exception as e:
        message =  '  Failed connection \n'+str(e)
      
      self.emit(QtCore.SIGNAL('testMessage'), message)


  def doApply(self,source):
    #print 'CServerSetupWindow.doApply', self.widgets['SERVERGROUP1'].widgets['serverList'].listWidget.count()
    invalidList = []
    for key in self.widgets.keys():
      self.widgets[key].updateModelFromView()
      #print 'CServerSetupWindow.doApply',key,self.widgets[key].widgets['mechanism'].currentIndex(),self.container.get(key).mechanism
      self.widgets[key].validate()
      if self.widgets[key].isValid is not None and not self.widgets[key].isValid:
        invalidList.append(key)

    #print 'CServerSetupWindow.doApply invalidList',invalidList
    
    if len(invalidList)>0:
      QtGui.QMessageBox.warning(self,'Server setup','Invalid data - not saved')
    else:
      self.container.save(source)
      #QtGui.QDialog.close(self)
      self.setAltSourceButton()
      import CCP4Modules
      CCP4Modules.JOBCONTROLLER().resetServersEnabled()

  def close(self):
    QtGui.QDialog.close(self)

  def help(self):
    CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='servers.html',newTab=True)

    
class CRemoteStatus(QtGui.QDialog):
  def __init__(self,parent):
    QtGui.QDialog.__init__(self,parent)
    self.setModal(False)
    self.setLayout(QtGui.QVBoxLayout())
    self.advice = QtGui.QLabel(self)
    self.layout().addWidget(self.advice)
    self.jobList = QtGui.QListWidget(self)   
    self.layout().addWidget(self.jobList )
    butLayout = QtGui.QHBoxLayout()
    for label,connect in [['Close',self.close]]:
      but = QtGui.QPushButton(label,self)
      self.connect(but,QtCore.SIGNAL('released()'),connect)
      butLayout.addWidget(but)
    self.layout().addLayout(butLayout)
    
  def load(self,text='',title=None,advice=None):
    if title is not None:
      self.setWindowTitle(title)
    else:
      self.setWindowTitle('')
    if advice is not None:
      self.advice.setText(advice)
    else:
      self.advice.setText('')
    self.jobList.clear()
    lineList = text.split('\n')
    for line in lineList: self.jobList.addItem(line)


class CPasswordEntry(QtGui.QDialog):
  def __init__(self,parent,label='Please reenter password',closeMessage='Ignore remote jobs'):
    QtGui.QDialog.__init__(self,parent)
    self.setModal(False)
    self.setLayout(QtGui.QVBoxLayout())
    self.layout().addWidget(QtGui.QLabel(label))
    self.passwordEntry = QtGui.QLineEdit(self)
    self.passwordEntry.setEchoMode(QtGui.QLineEdit.Password)
    self.layout().addWidget(self.passwordEntry)
    butLayout = QtGui.QHBoxLayout()
    for label,connect in [['Apply',self.sendPass],[closeMessage,self.close]]:
      but = QtGui.QPushButton(label,self)
      butLayout.addWidget(but)
      self.connect(but,QtCore.SIGNAL('released()'),connect)
    self.layout().addLayout(butLayout)

  def sendPass(self):
    passw = str(self.passwordEntry.text())
    #print 'sendPass',passw
    self.emit(QtCore.SIGNAL('passwordEntered'),passw)
    self.close()



class CListProcesses(QtGui.QDialog):
  COLUMNHEADERS = [ 'Project','Job','Taskname','Running process','Pid','Running time','Executable' ]
  def __init__(self,parent):
    QtGui.QDialog.__init__(self,parent)
    self.setWindowTitle('CCP4i2 List running jobs and processes')
    self.setLayout(QtGui.QVBoxLayout())
    self.tableWidget = QtGui.QTableWidget(self)
    self.tableWidget.setColumnCount(len(self.COLUMNHEADERS))
    self.tableWidget.setMinimumWidth(300)    
    self.tableWidget.setColumnWidth(1,50)
    self.tableWidget.setColumnWidth(4,50)
    self.tableWidget.setColumnWidth(6,250)
    self.layout().addWidget(self.tableWidget)
    
    butLayout = QtGui.QHBoxLayout()
    butLayout.addStretch(3)
    for label,connect in [['Update',self.load],['Help',self.help],['Close',self.close]]:
      but = QtGui.QPushButton(label,self)
      self.connect(but,QtCore.SIGNAL('released()'),connect)
      butLayout.addWidget(but)
    butLayout.addStretch(3)
    self.layout().addLayout(butLayout)
    self.projectNameCache = {}

  def projectName(self,projectId):
    if not self.projectNameCache.has_key(projectId):
      self.projectNameCache[projectId] = CCP4Modules.PROJECTSMANAGER().db().getProjectInfo(projectId,'projectname')
    return self.projectNameCache[projectId]

  def load(self):
    import time
    now = time.time()
    procDict = CCP4Modules.JOBCONTROLLER().listLocalProcesses(containsList=['ccp4'])
    #print 'CListProcesses.load',procDict
    runningJobs = CCP4Modules.PROJECTSMANAGER().db().getRunningJobs()
    
    self.tableWidget.clear()
    self.tableWidget.setRowCount(0)
    for ii in range(len(self.COLUMNHEADERS)):
      self.tableWidget.setHorizontalHeaderItem(ii,QtGui.QTableWidgetItem(self.COLUMNHEADERS[ii]))
      
    for jobId,jobNumber,taskName,projectId,processId,parentJobId in runningJobs:
      if parentJobId is None and processId is not None:
        taskTitle = CCP4Modules.TASKMANAGER().getTitle(taskName)
        #print 'CListProcesses.load',jobId,jobNumber,processId
        projectName = self.projectName(projectId)
        if procDict.has_key(processId):
          self.addRow(now,projectName,jobNumber,taskTitle,procDict[processId])
          for child in procDict[processId]['children']:
            if procDict.has_key(child):
              self.addRow(now,'','','',procDict[child])
        else:
          self.addRow(now,projectName,jobNumber,taskTitle,None)

  def addRow(self,now,projectName,jobNumber,taskName,procDict):     
     row = self.tableWidget.rowCount()
     self.tableWidget.insertRow(row)
     self.tableWidget.setItem(row,0, QtGui.QTableWidgetItem(str(projectName)))
     self.tableWidget.setItem(row,1, QtGui.QTableWidgetItem(str(jobNumber)))
     self.tableWidget.setItem(row,2, QtGui.QTableWidgetItem(str(taskName)))
     if procDict is None:
       self.tableWidget.setItem(row,3, QtGui.QTableWidgetItem('No process found'))
     else:
       self.tableWidget.setItem(row,3, QtGui.QTableWidgetItem(str(procDict['name'])))
       self.tableWidget.setItem(row,4, QtGui.QTableWidgetItem(str(procDict['pid'])))
       self.tableWidget.setItem(row,5, QtGui.QTableWidgetItem('%.1f' % (now-procDict['create_time'])))
       self.tableWidget.setItem(row,6, QtGui.QTableWidgetItem(str(procDict['exe'])))

  def help(self):
    CCP4Modules.WEBBROWSER().loadWebPage(helpFileName='tutorial',target='running')