# -*- mode:Python; explicit-buffer-name: "io.py<crlcore/helpers>" -*-
#
# This file is part of the Coriolis Software.
# Copyright (c) UPMC 2012-2018, All Rights Reserved
#
# +-----------------------------------------------------------------+ 
# |                   C O R I O L I S                               |
# |    C o r i o l i s  /  C h a m s   B u i l d e r                |
# |                                                                 |
# |  Author      :                    Jean-Paul Chaput              |
# |  E-mail      :            Jean-Paul.Chaput@lip6.fr              |
# | =============================================================== |
# |  Python      :   "./crlcore/helpers/io.py"                      |
# +-----------------------------------------------------------------+

import sys
import os
import os.path
import re
import traceback
try:
  from PyQt4.QtCore import Qt
  from PyQt4.QtGui  import QSizePolicy
  from PyQt4.QtGui  import QDialog
  from PyQt4.QtGui  import QPalette
  from PyQt4.QtGui  import QColor
  from PyQt4.QtGui  import QFont
  from PyQt4.QtGui  import QFontMetrics
  from PyQt4.QtGui  import QWidget
  from PyQt4.QtGui  import QFrame
  from PyQt4.QtGui  import QLabel
  from PyQt4.QtGui  import QPixmap
  from PyQt4.QtGui  import QPushButton
  from PyQt4.QtGui  import QTextEdit
  from PyQt4.QtGui  import QVBoxLayout
  from PyQt4.QtGui  import QHBoxLayout
  from PyQt4.QtGui  import QAction
  from PyQt4.QtGui  import QKeySequence
except Exception, e:
  try:
    from PyQt5.QtCore    import Qt
    from PyQt5.QtWidgets import QSizePolicy
    from PyQt5.QtWidgets import QDialog
    from PyQt5.QtGui     import QPalette
    from PyQt5.QtGui     import QColor
    from PyQt5.QtGui     import QFont
    from PyQt5.QtGui     import QFontMetrics
    from PyQt5.QtWidgets import QWidget
    from PyQt5.QtWidgets import QFrame
    from PyQt5.QtWidgets import QLabel
    from PyQt5.QtGui     import QPixmap
    from PyQt5.QtWidgets import QPushButton
    from PyQt5.QtWidgets import QTextEdit
    from PyQt5.QtWidgets import QVBoxLayout
    from PyQt5.QtWidgets import QHBoxLayout
    from PyQt5.QtWidgets import QAction
    from PyQt5.QtGui     import QKeySequence
  except e:
    print '[ERROR] helpers.io, neither PyQt4 nor PyQt5 is available.'
    sys.exit( 1 )
import helpers
from   Hurricane    import UpdateSession
import Viewer


# -------------------------------------------------------------------
# Class  :  "ErrorWidget".


class ErrorWidget ( QDialog ):

  def __init__ ( self, e ):
    QWidget.__init__ ( self, None )

    self.setWindowTitle( 'Error' )

    message = QLabel( e.getLinesAsString() )
    message.setAlignment( Qt.AlignLeft )
    message.setFont( QFont('Courier',10,QFont.Bold) )

    error = QLabel( '[ERROR]' )
    error.setAlignment( Qt.AlignLeft )
    font = error.font()
    font.setWeight( QFont.Bold )
    error.setFont( font )

    self._tryCont = QPushButton()
    self._tryCont.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed )
    self._tryCont.setText      ( 'Try to continue' )
    self._abort = QPushButton()
    self._abort.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed )
    self._abort.setText      ( 'Abort' )
    self._abort.setDefault   ( True )

    traceFont    = QFont('Courier',10,QFont.Normal)
    lineHeight   = QFontMetrics( traceFont ).height()
    traceText    = helpers.textStackTrace( e.trace, False, e.scriptPath )
    lineCount    = traceText.count( '\n' ) + 2
    minimumWidth = 400
    if Viewer.Graphics.isHighDpi(): minimumWidth = 2100
    self._trace = QTextEdit()
    self._trace.setReadOnly    ( True );
    self._trace.setLineWrapMode( QTextEdit.NoWrap );
    self._trace.setMinimumSize ( minimumWidth, lineCount*lineHeight );
    self._trace.setFont        ( traceFont )
    self._trace.setText        ( traceText )

    buttonLayout = QHBoxLayout()
    buttonLayout.addStretch( 1 )
    buttonLayout.addWidget ( self._tryCont )
    buttonLayout.addStretch( 1 )
    buttonLayout.addWidget ( self._abort )
    buttonLayout.addStretch( 1 )

    vLayout = QVBoxLayout()
    vLayout.addWidget ( error )
    vLayout.addStretch( 1 )
    vLayout.addWidget ( message )
    vLayout.addStretch( 1 )
    vLayout.addWidget ( self._trace )
    vLayout.addStretch( 1 )
    vLayout.addLayout ( buttonLayout )

    pixmap = QPixmap( ':/images/angry-birds-red.png' )
    pixmap = pixmap.scaledToWidth( 150 )
    icon = QLabel()
    icon.setPixmap( pixmap )

    hLayout = QHBoxLayout()
    hLayout.addWidget( icon )
    hLayout.addLayout( vLayout )
    self.setLayout( hLayout )

    self._tryCont.clicked.connect( self.accept )
    self._abort  .clicked.connect( self.reject )

    self._exitAction = QAction( '&Exit', self )
    self._exitAction.setStatusTip( 'Exit Coriolis' )
    self._exitAction.setShortcut ( QKeySequence('CTRL+Q') )
    self._exitAction.triggered.connect( self.reject )
    self.addAction( self._exitAction )

    self._closeAction = QAction( '&Close', self )
    self._closeAction.setStatusTip( 'Try to continue' )
    self._closeAction.setShortcut ( QKeySequence('CTRL+W') )
    self._closeAction.triggered.connect( self.reject )
    self.addAction( self._closeAction )
    
    return  

  def closeEvent ( self, event ):
    self.setResult( QDialog.Rejected )
    event.accept()
    return


# -------------------------------------------------------------------
# Class  :  "ErrorMessage".

class ErrorMessage ( Exception ):

    def __init__ ( self, code, *arguments ):
        self.scriptPath = None
        self.trace      = traceback.extract_stack()
        self._code      = code
        self._errors    = [ 'Malformed call to ErrorMessage()'
                          , '%s' % str(arguments) ]

        text = None
        if len(arguments) == 1:
          if isinstance(arguments[0],Exception): text = str(arguments[0]).split('\n')
          else:
            self._errors = arguments[0]
        elif len(arguments) > 1:
          sys.stdout.flush()
          text = list(arguments)

        if text:
          self._errors = []
          while len(text[0]) == 0: del text[0]

          lstrip = 0
          if text[0].startswith('[ERROR]'): lstrip = 8 

          for line in text:
            if line[0:lstrip] == ' '*lstrip or \
              line[0:lstrip-1] == '[ERROR]':
              self._errors += [ line[lstrip:] ]
            else:
              self._errors += [ line.lstrip() ]
          sys.stdout.flush()
        return

    def __str__ ( self ):
        if not isinstance(self._errors,list):
            return "[ERROR] %s" % self._errors

        formatted = "\n"
        for i in range(len(self._errors)):
            if i == 0: formatted += "[ERROR] %s" % self._errors[i]
            else:      formatted += "        %s" % self._errors[i]
            if i+1 < len(self._errors): formatted += "\n"
        return formatted

    def getLinesAsString ( self ):
        if not isinstance(self._errors,list): return self._errors

        lines = ''
        for line in self._errors: lines += line + '\n'
        return lines
    
    def addMessage ( self, message ):
        if not isinstance(self._errors,list):
            self._errors = [ self._errors ]
        if isinstance(message,list):
            for line in message:
                self._errors += [ line ]
        else:
            self._errors += [ message ]
        return

    def terminate ( self ):
        print self
        sys.exit( self._code )

    def _getCode ( self ): return self._code

    code = property(_getCode)

    @staticmethod
    def show ( code, *arguments ):
        e = ErrorMessage( code, *arguments )
        if Viewer.Graphics.get().isEnabled():
          tryCont = ErrorWidget( e ).exec_()
          if not tryCont: raise e 
        else:
          raise e
        return


def catch ( errorObject ):
    if isinstance(errorObject,ErrorMessage):
      em = errorObject
    else:
      em            = ErrorMessage( 2, errorObject )
      em.trace      = traceback.extract_tb( sys.exc_info()[2] )
     #em.scriptPath = __file__

    if Viewer.Graphics.get().isEnabled():
      tryCont = ErrorWidget( em ).exec_()
    print em
    print helpers.textStackTrace( em.trace, True, em.scriptPath )

    if UpdateSession.getStackSize() > 0: UpdateSession.close()
    return

    


# -------------------------------------------------------------------
# Class  :  "WarningMessage".


class WarningMessage ( Exception ):

    def __init__ ( self, message ):
        self._warnings = message
        return

    def __str__ ( self ):
        if not isinstance(self._warnings,list):
            return "[WARNING] %s" % self._warnings

        formatted = "\n"
        for i in range(len(self._warnings)):
            if i == 0: formatted += "[WARNING] %s" % self._warnings[i]
            else:      formatted += "          %s" % self._warnings[i]
            if i+1 < len(self._warnings): formatted += "\n"
        return formatted