"""
     CCP4WebView.py: CCP4 GUI Project
     Copyright (C) 2009-2010 University of York

     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.
 
     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 Jan 2010 - Copied from CCP4mg
"""
##@package CCP4WebView (QtWebKit) Web browser 'plugin' to view web pages
from PyQt4 import QtGui,QtCore,QtWebKit
from CCP4ErrorHandling import *
import CCP4Modules,CCP4Config

def setGlobalSettings():
  webSettings = QtWebKit.QWebSettings.globalSettings()
  #print 'setGlobalSetting webSettings',webSettings,key,value
  preferences = CCP4Modules.PREFERENCES()
  webSettings.setFontSize(QtWebKit.QWebSettings.DefaultFontSize,preferences.REPORT_FONT_SIZE)
  webSettings.setFontSize(QtWebKit.QWebSettings.DefaultFixedFontSize,preferences.REPORT_FONT_SIZE)
  webSettings.setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled,True)
  for window in CWebView.Instances:
    window.setLoggraphFont()

def isAlive(qobj):
  import sip
  try:
    sip.unwrapinstance(qobj)
  except RuntimeError:
    return False
  return True
  

class CWebPage(QtWebKit.QWebPage):
  def __init__(self,parent=None):
    QtWebKit.QWebPage.__init__(self,parent)
    #self.setLinkDelegationPolicy(QtWebKit.QWebPage.DelegateAllLinks)
    self.setForwardUnsupportedContent(True)
    self.settings().setAttribute(QtWebKit.QWebSettings.JavascriptEnabled,1)
    self.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled,1)

  def shouldInterruptJavaScript(self):
    # Reimplemented to prevent accasianally seen message box (by Phil) when creating DR reports
    # Implied from http://forum.qt.io/topic/21387/maximum-duration-for-a-script/3
    #http://stackoverflow.com/questions/9284511/reimplement-the-shouldinterruptjavascript-in-qt-c
    print 'CWebPage.shouldInterruptJavaScript called'
    import traceback
    traceback.print_stack()
    #QApplication::processEvents(QEventLoop::AllEvents, 42)
    CCP4Modules.QTAPPLICATION().processEvents(QtCore.QEventLoop.AllEvents,42)
    return False


  def javaScriptConsoleMessage ( self, message, lineNumber, sourceID ) :
    return
    '''
    print " ############ JAVASCRIPT MESSAGE ########### "
    print "lineNumber: ", lineNumber, " sourceID: ", sourceID, " message: ", message
    print " ########################################### "
    '''


  def acceptNavigationRequest(self,frame=None,request=None,type=None):
    import os
    #print 'CWebPage.acceptNavigationRequest',request,request.url().path(),request.url().isLocalFile()
    if CCP4Modules.PREFERENCES().EXTERNAL_FILES_IN_EXTERNAL_BROWSER and not request.url().isLocalFile():
      rv = QtGui.QDesktopServices.openUrl(request.url())
      if not rv:
        QtGui.QMessageBox.warning(None,'Display url','Attempting to display external URL using non-CCP4i2 web browser failed')
      return False
    path = str(request.url().path())
    #print 'acceptNavigationRequest path',path
    format = CCP4Modules.MIMETYPESHANDLER().formatFromFileExt(path)
    #print 'CWebPage.acceptNavigationRequest',request.url().path(),format
    if format and not CCP4Modules.MIMETYPESHANDLER().useWebBrowser(format):
      self.emit(QtCore.SIGNAL("CustomMimeTypeRequested"),request.url())
      return False    # CCP4WebBrowser is not taking responsibility
    if self.parent().blockLoading():
      self.emit(QtCore.SIGNAL("NavigationRequest"),request.url())
      return False
    # This is broken if setHtml() has been used to set the page text and give
    # a local base href - the base href is returned by request.url()
    #self.view().extractCCP4Data(request.url())
    return True
        
  def clickOn(self,pos):
    #print 'CWebpage.clickOn', pos.x(),pos.y()
    # send a mouse click event to the web page
    event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
    CCP4Modules.QTAPPLICATION().sendEvent(self, event)
    event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonRelease, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
    CCP4Modules.QTAPPLICATION().sendEvent(self, event)

   
	
class CWebView(QtWebKit.QWebView):

    ERROR_CODES= { 101 : { 'description' : 'Error creating CCP4i2 Web Plugins' }            }

    Instances = set()

    def __init__(self,parent=None,blockLoading=False):
      QtWebKit.QWebView.__init__(self,parent)
      CWebView.Instances.add(self)
      self.connect(self,QtCore.SIGNAL("destroyed(QObject*)"),CWebView.updateInstances)
      self._blockLoading = blockLoading
      self.fileName = None
      self.report = None
      self.resetScroll = None
      self.mgpage = CWebPage(self)
      self.customContextMenu = None
      self.setPage(self.mgpage)
      self.connect(self.mgpage,QtCore.SIGNAL("CustomMimeTypeRequested"),self.CustomMimeTypeRequested)
      self.connect(self.mgpage,QtCore.SIGNAL("statusBarMessage(const QString&)"),self.StatusBarMessage)
      self.connect(self.mgpage,QtCore.SIGNAL("linkHovered(const QString &, const QString &, const QString &)"),self.LinkHovered)
      self.connect(self,QtCore.SIGNAL("linkClicked(const QUrl &)"),self.linkClicked)
      self.connect(self,QtCore.SIGNAL("loadFinished(bool)"),self.LoadFinished)
      self.connect(self,QtCore.SIGNAL("loadStarted()"),self.LoadStarted)
      shortcut = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+C", "Copy")),self)
      self.connect(shortcut,QtCore.SIGNAL("activated()"),self.copyHighlighted)
      """
      #So we need a preferences pane for browser....
      self.settings().setFontSize(QtWebKit.QWebSettings.MinimumFontSize,9)
      self.settings().setFontSize(QtWebKit.QWebSettings.DefaultFontSize,16)
      self.settings().setFontSize(QtWebKit.QWebSettings.DefaultFixedFontSize,18)
      self.settings().setFontFamily(QtWebKit.QWebSettings.StandardFont,"Times")
      """
      self.setTarget('')
      self.connect(self.mgpage,QtCore.SIGNAL("unsupportedContent(QNetworkReply*)"),self.handleUnsupportedContent)
	  
      
      #Create Qt web plugin factory
      try:
        import CCP4WebPluginFactory
        factory = CCP4WebPluginFactory.CWebPluginFactory(self)
        self.page().setPluginFactory(factory)
      except CException as e:
        print 'CCP4WebView Error creating web plugin factory'
      except Exception as e:
        print 'CCP4WebView Error creating web plugin factory'

      '''
        def sizeHint(self):
        return QtCore.QSize(400,600)
     '''
        
    @staticmethod
    def updateInstances(qobj):
      CWebView.Instances = set([window for window in CWebView.Instances if isAlive(window)])
   

    def objectPath(self):
      if self.mgpage is None:
        return ''
      else:
        return str(self.mgpage.objectName())

    def blockLoading(self):
	  return self._blockLoading
		
    def createWindow(self,type=None):
      view = CWebView()
      self.emit(QtCore.SIGNAL("NewWindowRequested"),view)
      return view

    def LoadStarted(self,):
      icon = QtGui.QIcon()
      self.emit(QtCore.SIGNAL("IconReady"),(icon,self))
		

    def setTarget(self,target=None):
      self.target=target

    def LoadFinished(self,ok=None):
      icon = self.icon()
	  #print "CWebView.LoadFinished", self.target,ok,self.url().toLocalFile()
      if ok: return
      if self.target:
        #print 'currentFrame',self.page().mainFrame(),self.target.strip('#')
        self.page().mainFrame().scrollToAnchor(self.target.strip('#'))
        self.setTarget('')
	    #self.load(myurl)
      self.emit(QtCore.SIGNAL("IconReady"),(icon,self))
      self.subJobReport = CSubJobReport(self)
      self.page().currentFrame().addToJavaScriptWindowObject('SubJobReport',self.subJobReport)
      self.setLoggraphFont()
      if self.resetScroll is not None:
        self.page().mainFrame().setScrollBarValue(QtCore.Qt.Vertical,self.resetScroll)
        self.resetScroll = None

	
	def contextMenuEvent(self,event):
	  #print 'CWebView.contextMenuEvent',event.pos().x(),event.pos().y()
	  hitTestResult = None
	  alt_words = []
	  frame = self.page().frameAt(event.pos())
	  if frame: hitTestResult = frame.hitTestContent(event.pos())
	  #print 'CWebView.contextMenuEvent',frame,hitTestResult
	  #print 'CWebView.contextMenuEvent hitTestResult alt text',str(hitTestResult.alternateText())
	  alt_text = hitTestResult.alternateText()
	  if not alt_text.isEmpty():
	    alt_words = (str(alt_text)).split()
	  #print 'CWebView.contextMenuEvent alt text',alt_words

	  if hitTestResult: webElement = hitTestResult.element()

	  if len(alt_words)>=2 and alt_words[0] == 'x-ccp4-widget/CDataFileView':
	    if not self.customContextMenu:
	       import CCP4GuiUtils
	       self.customContextMenu = QtGui.QMenu(self)
	       CCP4GuiUtils.populateMenu(self,self.customContextMenu,['copy','view','help'],default_icon='')
	    self.customContextMenu.popup(event.globalPos())
	    event.accept()
          else:
	    QtWebKit.QWebView.contextMenuEvent(self,event)

	def getActionDef(self,name=''):

          if name == 'copy':
            return dict (
              text = self.tr("Copy"),
              tip = self.tr('Copy data'),
              #enabled = e,
              slot = self.dummyHandler
           )
          if name == 'help':
            return dict (
            text = self.tr("Help"),
            tip = self.tr('Help'),
            slot = self.dummyHandler
          )
          if name == 'view':
           return dict (
           text = self.tr("View"),
           tip = self.tr('View data'),
           slot = self.dummyHandler
          )


	def dummyHandler(self):
	   pass

   
    def CustomMimeTypeRequested(self,url=None):
      if url: self.emit(QtCore.SIGNAL("CustomMimeTypeRequested"),url)

    def copyHighlighted(self):
	  CCP4Modules.QTAPPLICATION().clipboard().setText(self.selectedText())


    def handleUnsupportedContent(self,qnr):
      #print 'handleUnsupportedContent',qnr,qnr.url(),'scheme',str(qnr.url().scheme()),'path',str(qnr.url().path())
	   pass
                
    def LinkHovered(self,link,title,content):
	  self.emit(QtCore.SIGNAL("StatusBarMessage"),(link))

    def linkClicked(self,url):
	  print 'CWebView.linkClicked',url.toLocalFile(),url.fragment()
	  self.load(url)

    def StatusBarMessage(self,message=None):
	  self.emit(QtCore.SIGNAL("StatusBarMessage"),(message))

    def isPrintable(self):
	  return 1
  
    def isSaveable(self):
	  return 1
  
    def isSearchable(self):
	  return 1
        
    def isRunable(self):
	  return 0

    def findText(self,subString='',direction=1,caseSensitive=0,wrapAroundDocument=1,highlightAll=0):
	  flags = 0
	  #if highlightAll:  flags = flags | QtWebKit.QWebPage.HighlightAllOccurrences
	  if wrapAroundDocument: flags = flags | QtWebKit.QWebPage.FindWrapsAroundDocument
	  if direction<0: flags = flags |  QtWebKit.QWebPage.FindBackward 
	  if caseSensitive: flags = flags | QtWebKit.QWebPage.FindCaseSensitively
	  #print 'CWebView.findText',subString,flags
	  if flags:
	    return self.mgpage.findText(QtCore.QString(subString),flags)
          else:
	    return self.mgpage.findText(QtCore.QString(subString))
    
    def getFileExt(self):
	  import CCP4CustomMimeTypes
          return   CCP4CustomMimeTypes.MimeTypesHandler().getMimeTypeInfo(name='text/html',info='fileExtensions')

    def getLabel(self):
      import CCP4CustomMimeTypes
      return   CCP4CustomMimeTypes.MimeTypesHandler().getMimeTypeInfo(name='text/html',info='description')

    def Save(self,fileName):
	  import CCP4Utils
	  CCP4Utils.saveFile(fileName,str(self.mgpage.currentFrame().toHtml()))

    def title(self):
      t = QtWebKit.QWebView.title(self)
      return str(t)

    def load(self,url,reportErrors=False):
      self.extractCCP4Data(url,reportErrors=reportErrors)
      #print 'WebView.load',str(url.toLocalFile()),self.report,self.report.resetBaseHref
      self.editHTTPPort(self.fileName)
      block = self._blockLoading
      self._blockLoading = False
      if self.report is None or self.report.resetBaseHref is None:
        QtWebKit.QWebView.load(self,url)
      else:
        url =  QtCore.QUrl.fromLocalFile(self.report.resetBaseHref)
        QtWebKit.QWebView.load(self,url)
        #print 'CWebView.load fragment',url.fragment()
      if url.isLocalFile():
        self.fileName = str(url.toLocalFile())
      else:
        self.fileName = str(url.toString())
      self._blockLoading = block
      #print 'CWebView.load _blockLoading',self._blockLoading

    def attemptImageLoad(self):
      self.page().mainFrame().evaluateJavaScript('''
var imgElements = document.getElementsByTagName('img');
for (var i=0; i<imgElements.length;i++) {
   var imgElement = imgElements[i];
   var imgPath = imgElement.getAttribute('src');
   var d=new Date();
   $(imgElement).attr('src',imgPath+'?'+d.getTime());
}
''')
    def reloadReportData(self):
      nUpdatedAsQVariant = self.page().mainFrame().evaluateJavaScript('''
reloadReportData();
''')
      return nUpdatedAsQVariant.toInt()
    
    def reload(self):
      self.resetScroll = self.page().mainFrame().scrollBarValue(QtCore.Qt.Vertical)
      block = self._blockLoading
      self._blockLoading = False
      #if self.report is None or self.report.resetBaseHref is None:
      #  QtWebKit.QWebView.reload(self)
      #else:
      #  QtWebKit.QWebView.load(self,QtCore.QUrl.fromLocalFile(+self.report.resetBaseHref))
      self.load(self.url())
      self._blockLoading = block

    def loadHtmlText(self):
      # http://stackoverflow.com/questions/2727080/how-to-get-qwebkit-to-display-image
      # answer from badcat that security feature does not allow local disk baseRef in setHtml
      print 'CWebView loading text from',self.fileName

    def editHTTPPort(self,fileName,port=None):
          import os,re
          import CCP4Utils
          #print 'CWebView.editHTTPPort',fileName
          if len(fileName)<0: return
          try:
            text = CCP4Utils.readFile(fileName)
          except:
            return False
          if port is None: port = CCP4Modules.HTTPSERVER().port
          changed = False
          #print 'CWebView.editHTTPPort',re.search('../../report_files',text)
          if re.search('../../report_files',text) is not None:
            changed = True
            text = re.sub('../../report_files','http://127.0.0.1:'+str(port)+'/report_files',text)
            #print 'CWebView.editHTTPPort after first sub',text[0:500]
          else:
            matchObj = re.search('(.*)127.0.0.1:(.*)/report_files(.*)',text)
            #print 'CWebView.editHTTPPort matchObj',matchObj
            if matchObj is not None:
              oldPort = matchObj.groups()[1]
              #print 'CWebView.editHTTPPort oldPort',oldPort
              if oldPort != str(port):
                changed = True
                text = re.sub('127.0.0.1:'+oldPort+'/report_files','127.0.0.1:'+str(port)+'/report_files',text)
          if changed:
            CCP4Utils.backupFile(fileName,delete=True)
            CCP4Utils.saveFile(fileName,text)
          #print 'CWebView.editHTTPPort changed',changed
          return changed
          
	  
    def extractCCP4Data(self,url,reportErrors=False):
	  import os
	  html_exts = CCP4Modules.MIMETYPESHANDLER().getMimeTypeInfo('text/html','fileExtensions')
	  self.report = None
          self.fileName = os.path.normpath(str(url.toLocalFile()))
	  ext = os.path.splitext(self.fileName)[1]
	  if len(ext)>1: ext=ext[1:]
	  #print 'CWebView.extractCCP4Data',self.fileName
          if html_exts.count(ext) and os.path.exists(self.fileName):
            self.lastModTime = os.path.getmtime(self.fileName)
	    import CCP4Report
            self.report = CCP4Report.CReport()
	    try:
	      self.report.loadFromXmlFile(self.fileName)
	      #print 'CWebView.extractCCP4Data: Loaded CCP4 data:',self.report.containsCCP4Data()
	    except CException as e:
	      if reportErrors: e.warningMessage(windowTitle=self.windowTitle())
	  else:
	    self.fileName = ''
            self.lastModTime = None

            
    def isFileModified(self):
          import os
          if len(self.fileName)==0: return False
          modTime =  os.path.getmtime(self.fileName)
          if modTime > self.lastModTime:
            return True
          else:
            return False
       
	  
    def close(self):
	  pass

    def clear(self):
	  self.mgpage = CWebPage(self)
	  self.setPage(self.mgpage)

    def handleTabbedOpen(self):
	  pass
    def handleTabbedClosed(self):
	  pass
        
    def setLoggraphFont(self,font={}):
          #print 'setLoggraphFont',CCP4Modules.PREFERENCES().REPORT_FONT_SIZE-2,font
          fontSel =  { 'family' : 'Lucida Sans Unicode', 'size' : int(CCP4Modules.PREFERENCES().REPORT_FONT_SIZE-2)}
          fontSel.update(font)
          import MGQTmatplotlib
          loggraphList = self.findChildren(MGQTmatplotlib.LogGraph)
          #print 'CWebPage.setLoggraphFont',loggraphList
          for loggraph in loggraphList:
            loggraph. applyPreferences(titleFontSel = fontSel,axesFontSel= fontSel,
                               axesLabelFontSel=fontSel, legendFontSel = fontSel )

    
    def emitDownloadRequest(self,jobId,dataName):
          print 'CWebView.emitDownloadRequest',jobId,dataName
          self.emit(QtCore.SIGNAL('csvDownloadRequest'),jobId,dataName)
          
 

class CSubJobReport(QtCore.QObject):
  def __init__(self,parent):
    QtCore.QObject.__init__(self,parent)
    
  @QtCore.pyqtSlot(unicode)
  def load(self,idText):
    '''
    Invoked from xreport.js to merge sub-job report into paret report
    '''
    reportErr = True
    if idText[0:5] != 'jobId':
      print 'ERROR in load sub-job report - CSubJobReport.load did not receive jobId'
      return
    jobId = int(idText[5:])

    import CCP4DbUtils,CCP4ReportGenerator,os
    openJob = CCP4DbUtils.COpenJob(jobId=jobId)
    generator = CCP4ReportGenerator.CReportGenerator(jobId=openJob.jobId,jobStatus=openJob.status)
    #self.connect(generator,QtCore.SIGNAL('FinishedPictures'),self.handleMgFinished)
    if CCP4Config.DEVELOPER():
      reportFile, newPageOrNewData = generator.makeReportFile()
    try:
      reportFile, newPageOrNewData = generator.makeReportFile()
    except CException as e:
      if reportErr and e.maxSeverity()>SEVERITY_WARNING:
        e.warningMessage()
    except Exception as e:
      if reportErr:
        QtGui.QMessageBox.warning(self,self.parent().windowTitle(),'Unknown error creating report file for job number '+str(openJob.jobNumber))
    #print 'CSubJobReport.',reportFile
    if os.path.exists(reportFile):
      err = self.merge(reportFile,idText)
      

  def merge(self,reportFile,idText):
    from lxml import etree
    import CCP4Utils

    '''
    parentTree = etree.parse(parentFile)
    print 'mergeIntoParent jobId',self.jobId
    # Find the span-link created by CCP4ReportParser.foldLinkLine
    path = 'body/div[@class="sub-job-list"]/span[@class="folder_link"]/a[@id="jobId'+str(self.jobId)+'"]'
    aEle = parentTree.xpath(path)
    print 'mergeIntoParent aEle',aEle
    if len(aEle) == 0: return CErrorReport(self.__class__,5,parentFile)
    aEle = aEle[0]
    label = aEle.text
    print 'mergeIntoParent',label
    insertEle = aEle.xpath('../..')[0]
    insertIndex = insertEle.index(aEle.xpath('..')[0])
    '''

    #myTree =  etree.parse(reportFile)
    myTree = CCP4Utils.openFileToEtree(reportFile)
    # Load the ccp4_data while we have the file as xml
    self.parent().report.loadFromEtree(myTree)
    # Convert body of sub-job report html to a div.subjob
    body = myTree.xpath('./body')[0]
    body.tag ='div'
    body.set('id',str(idText))
    body.set('class','subjob')
    subJobText = etree.tostring(body)
    #print 'CSubJobReport.merge subJobText(cut)',subJobText[0:200]


    ele = self.parent().page().currentFrame().findFirstElement('body div#'+idText)
    #print 'CSubJobReport.merge div ele',ele,ele.isNull()
    if not ele.isNull():
      ele.replace(subJobText)
      ele.setStyleProperty('display','block')
      #print 'CSubJobReport.merge done replace'
      #self.parent().reload()
      #print 'CSubJobReport.merge done reload'
      text = self.parent().page().currentFrame().toHtml().__str__()
      import CCP4Utils
      CCP4Utils.saveFile(self.parent().fileName+'.tmp',text,overwrite=True)
      #self.parent().fileName = self.parent().fileName+'.tmp'
      #self.parent().reolad()