###############################################################################
##
## Copyright (C) 2011-2014, NYU-Poly.
## Copyright (C) 2006-2011, University of Utah. 
## All rights reserved.
## Contact: contact@vistrails.org
##
## This file is part of VisTrails.
##
## "Redistribution and use in source and binary forms, with or without 
## modification, are permitted provided that the following conditions are met:
##
##  - Redistributions of source code must retain the above copyright notice, 
##    this list of conditions and the following disclaimer.
##  - Redistributions in binary form must reproduce the above copyright 
##    notice, this list of conditions and the following disclaimer in the 
##    documentation and/or other materials provided with the distribution.
##  - Neither the name of the University of Utah nor the names of its 
##    contributors may be used to endorse or promote products derived from 
##    this software without specific prior written permission.
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
## THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
## OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
## OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
## ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
##
###############################################################################

""" The file describes the parameter tree view that allows changing the alias
of parameters

QAliasParameterView
"""
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import pyqtSignal, pyqtSlot
from vistrails.core.inspector import PipelineInspector
from vistrails.core.vistrail.module_param import ModuleParam
from vistrails.gui.common_widgets import QSearchTreeWindow, QSearchTreeWidget
from vistrails.gui.paramexplore.pe_pipeline import QAnnotatedPipelineView
from vistrails.gui.vistrails_palette import QVistrailsPaletteInterface
from vistrails.gui.theme import CurrentTheme
from vistrails.core.utils import InstanceObject

################################################################################
class QAliasParameterView(QtGui.QWidget, QVistrailsPaletteInterface):
    
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.set_title("Mashup Pipeline")
        layout = QtGui.QVBoxLayout()
        self.parameter_panel = QAliasParameterPanel()
        font = QtGui.QFont("Arial", 11, QtGui.QFont.Normal)
        font.setItalic(True)
        label = QtGui.QLabel("Double-click a parameter to change alias")
        label.setFont(font)
        param_group = QtGui.QGroupBox(self.parameter_panel.windowTitle())
        g_layout = QtGui.QVBoxLayout()
        g_layout.setMargin(0)
        g_layout.setSpacing(2)
        g_layout.addWidget(label)
        g_layout.addWidget(self.parameter_panel)
        param_group.setLayout(g_layout)
        layout.addWidget(param_group)
        
        self.pipeline_view = QAnnotatedPipelineView()
        self.pipeline_view.setReadOnlyMode(True)
        p_view_group = QtGui.QGroupBox(self.pipeline_view.windowTitle())
        g_layout = QtGui.QVBoxLayout()
        g_layout.setMargin(0)
        g_layout.setSpacing(0)
        g_layout.addWidget(self.pipeline_view)
        p_view_group.setLayout(g_layout)
        layout.addWidget(p_view_group)
        self.setLayout(layout)
        
        self.parameter_panel.treeWidget.aliasChanged.connect(self.aliasChanged)
        
    def updateMshpController(self, mshpController):
        from vistrails.gui.vistrails_window import _app
        self.mshpController = mshpController
        self.parameter_panel.set_pipeline(
                        self.mshpController.vtPipeline)
        self.pipeline_view.set_controller(self.mshpController.vtController)
        self.mshpController.vtController.current_pipeline_view = \
                                                            self.pipeline_view
        self.pipeline_view.scene().current_pipeline = self.mshpController.vtPipeline
        self.mshpController.vtController.current_pipeline = self.mshpController.vtPipeline
        
        #print "**** should update mashup pipeline view "
        
        #self.pipeline_view.scene().setupScene(self.mshpController.vtPipeline)
        self.pipeline_view.scene().clear()
        self.pipeline_view.version_changed()
        self.pipeline_view.zoomToFit()
        self.pipeline_view.updateAnnotatedIds(
                        self.mshpController.vtPipeline)
        #_app.notify('mashup_pipeline_view_set')
        
    def updateMshpVersion(self, version):
        #print "will update alias param view"
        self.parameter_panel.set_pipeline(
                        self.mshpController.vtPipeline)
        self.pipeline_view.version_changed()
        
    def zoomToFit(self):
        if self.pipeline_view:
            self.pipeline_view.zoomToFit()
            
    def aliasChanged(self, param):
        from vistrails.gui.vistrails_window import _app
        _app.notify('alias_changed', param)
        
################################################################################

class QAliasParameterPanel(QSearchTreeWindow):
    """
    QAliasParameterPanel is a special widget for displaying methods and 
    parameters inside a pipeline that allow changing aliases
    
    """
    def createTreeWidget(self):
        """ createTreeWidget() -> QModuleTreeWidget
        Return the search tree widget for this window
        
        """
        self.setWindowTitle("Parameter Aliases")
        return QAliasParameterTreeWidget(self)

    def set_pipeline(self, pipeline):
        self.pipeline = pipeline
        self.treeWidget.updateFromPipeline(pipeline)
        
################################################################################
class QAliasParameterTreeWidget(QSearchTreeWidget):
    """
    QAliasParameterTreeWidget is a subclass of QSearchTreeWidget to display all
    Vistrails Module
    
    """
    #signals
    aliasChanged = pyqtSignal(InstanceObject)
    
    def __init__(self, parent=None):
        """ QAliasParameterTreeWidget(parent: QWidget) -> QParameterTreeWidget
        Set up size policy and header

        """
        QSearchTreeWidget.__init__(self, parent)
        self.header().hide()
        self.setRootIsDecorated(False)
        self.delegate = QAliasParameterTreeWidgetItemDelegate(self, self)
        self.setItemDelegate(self.delegate)
        self.aliasNames = []
        self.itemDoubleClicked.connect(self.changeAlias)
    def updateFromPipeline(self, pipeline):
        """ updateFromPipeline(pipeline: Pipeline) -> None
        Read the list of aliases and parameters from the pipeline
        
        """
        self.clear()
        if not pipeline:
            return

        # Update the aliases
#        if len(pipeline.aliases)>0:
#            aliasRoot = QParameterTreeWidgetItem(None, self, ['Aliases'])
#            aliasRoot.setFlags(QtCore.Qt.ItemIsEnabled,
#                               )
#            for (alias, info) in pipeline.aliases.iteritems():
#                ptype, pId, parentType, parentId, _ = info
#                parameter = pipeline.db_get_object(ptype, pId)
#                v = parameter.strValue
#                aType = parameter.type
#                aIdentifier = parameter.identifier
#                aNamespace = parameter.namespace
#                label = ['%s = %s' % (alias, v)]
#                pInfo = InstanceObject(type=aType,
#                                      identifier=aIdentifier,
#                                      namespace=aNamespace,
#                                      value=v,
#                                      id=pId,
#                                      dbtype=ptype,
#                                      parent_dbtype=parentType,
#                                      parent_id=parentId,
#                                      is_alias=True)
#                aliasItem = QParameterTreeWidgetItem((alias, [pInfo]),
#                                                     aliasRoot, label)
#            aliasRoot.setExpanded(True)
            
        # Now go through all modules and functions
        self.aliasNames = pipeline.aliases.keys()
        inspector = PipelineInspector()
        inspector.inspect_ambiguous_modules(pipeline)
        sortedModules = sorted(pipeline.modules.iteritems(),
                               key=lambda item: item[1].name)
        for mId, module in sortedModules:
            if len(module.functions)>0:
                mLabel = [module.name]
                moduleItem = None
                for fId in xrange(len(module.functions)):
                    function = module.functions[fId]
                    if len(function.params)==0: continue
                    if moduleItem==None:
                        if inspector.annotated_modules.has_key(mId):
                            annotatedId = inspector.annotated_modules[mId]
                            moduleItem = QAliasParameterTreeWidgetItem(annotatedId,
                                                                       self, mLabel)
                        else:
                            moduleItem = QAliasParameterTreeWidgetItem(None,
                                                                  self, mLabel)
                    #v = ', '.join([p.strValue for p in function.params])
                    label = ['%s'%function.name]
                    
                    pList = [InstanceObject(type=function.params[pId].type,
                                           identifier=function.params[pId].identifier,
                                           namespace=function.params[pId].namespace,
                                           value=function.params[pId].strValue,
                                           id=function.params[pId].real_id,
                                           dbtype=ModuleParam.vtType,
                                           parent_dbtype=function.vtType,
                                           parent_id=function.real_id,
                                           alias=function.params[pId].alias,
                                           mId=mId)
                             for pId in xrange(len(function.params))]
                    mName = module.name
                    if moduleItem.parameter!=None:
                        mName += '(%d)' % moduleItem.parameter
                    fName = '%s :: %s' % (mName, function.name)
                    mItem = QAliasParameterTreeWidgetItem((fName, pList),
                                                     moduleItem,
                                                     label)
                if moduleItem:
                    moduleItem.setExpanded(True)
                    
    @pyqtSlot(QtGui.QTreeWidgetItem, int)    
    def changeAlias(self, item, column ):
        """ itemClicked(item:  , column: int) -> None        
        If mouse click on the item, show up a dialog to change/add
        the alias name
        
        """
        if isinstance(item.parameter, InstanceObject):
            (text, ok) = QtGui.QInputDialog.getText(self,
                                                    'Set Parameter Alias',
                                                    'Enter the parameter alias',
                                                    QtGui.QLineEdit.Normal,
                                                    item.parameter.alias)
            while ok and str(text) in self.aliasNames:
                msg =" This alias is already being used.\
 Please enter a different parameter alias "
                (text, ok) = QtGui.QInputDialog.getText(self,
                                                        'Set Parameter Alias',
                                                        msg,
                                                        QtGui.QLineEdit.Normal,
                                                        text)
            if ok and item.parameter.alias != str(text):
                item.parameter.alias = str(text)
                item.updateAlias()
                self.aliasChanged.emit(item.parameter)
            
            
        
class QAliasParameterTreeWidgetItemDelegate(QtGui.QItemDelegate):
    """    
    QAliasParameterTreeWidgetItemDelegate will override the original
    QTreeWidget paint function to draw buttons for top-level item
    similar to QtDesigner. This mimics
    Qt/tools/designer/src/lib/shared/sheet_delegate, which is only a
    private class from QtDesigned.
    
    """
    def __init__(self, view, parent):
        """ QParameterTreeWidgetItemDelegate(view: QTreeView,
                                          parent: QWidget)
                                          -> QParameterTreeWidgetItemDelegate
        Create the item delegate given the tree view
        
        """
        QtGui.QItemDelegate.__init__(self, parent)
        self.treeView = view

    def paint(self, painter, option, index):
        """ painter(painter: QPainter, option QStyleOptionViewItem,
                    index: QModelIndex) -> None
        Repaint the top-level item to have a button-look style
        
        """
        model = index.model()
        if (model.parent(index).isValid()==False or 
            model.parent(index).parent().isValid()==False):
            style = self.treeView.style()
            r = option.rect
            textrect = QtCore.QRect(r.left() + 10,
                                    r.top(),
                                    r.width() - 10,
                                    r.height())
            font = painter.font()
            font.setBold(True)
            painter.setFont(font)
            text = option.fontMetrics.elidedText(
                model.data(index, QtCore.Qt.DisplayRole),
                QtCore.Qt.ElideMiddle, 
                textrect.width()-10)
            style.drawItemText(painter,
                               textrect,
                               QtCore.Qt.AlignLeft,
                               option.palette,
                               self.treeView.isEnabled(),
                               text)
            painter.setPen(QtGui.QPen(QtCore.Qt.black))
            fm = QtGui.QFontMetrics(font)
            size = fm.size(QtCore.Qt.TextSingleLine, text)
            #painter.drawLine(textrect.left()-5,
            #                 textrect.bottom()-1,
            #                 textrect.left()+size.width()+5,
            #                 textrect.bottom()-1)

            annotatedId = model.data(index, QtCore.Qt.UserRole+1)            
            if annotatedId:
                idRect = QtCore.QRect(
                    QtCore.QPoint(textrect.left()+size.width()+5,
                                  textrect.top()),
                    textrect.bottomRight())
                QAnnotatedPipelineView.drawId(painter, idRect,
                                              annotatedId,
                                              QtCore.Qt.AlignLeft |
                                              QtCore.Qt.AlignVCenter)
        else:
            QtGui.QItemDelegate.paint(self, painter, option, index)

    def sizeHint(self, option, index):
        """ sizeHint(option: QStyleOptionViewItem, index: QModelIndex) -> None
        Take into account the size of the top-level button
        
        """
        return (QtGui.QItemDelegate.sizeHint(self, option, index) +
                QtCore.QSize(2, 2))
            

class QAliasParameterTreeWidgetItem(QtGui.QTreeWidgetItem):
    """
    QParameterTreeWidgetItem represents module on QParameterTreeWidget
    
    """
    def __init__(self, info, parent, labelList):
        """ QAliasParameterTreeWidgetItem(info: (str, []),
                                     parent: QTreeWidgetItem
                                     labelList: string)
                                     -> QParameterTreeWidget
                                     
        Create a new tree widget item with a specific parent and
        labels. info describing a set of parameters as follow:
        (name, [InstanceObject]):
           name  = Name of the parameter set (alias or function)
        If this item is a top-level item, info can either be None or
        an integer specifying the annotated id of this module

        """
        self.parameter = info
        QtGui.QTreeWidgetItem.__init__(self, parent, labelList)
        if isinstance(self.parameter, int):
            self.setData(0, QtCore.Qt.UserRole+1, self.parameter)
        elif isinstance(self.parameter, tuple):
            for param in self.parameter[1]:
                label = ['']
                item = QAliasParameterTreeWidgetItem(param, self, label)
                item.setFlags(QtCore.Qt.ItemIsEnabled|
                              QtCore.Qt.ItemIsSelectable)
                item.updateAlias()
            self.setExpanded(True)
    
    def updateAlias(self):    
        if isinstance(self.parameter, InstanceObject):
            if self.parameter.alias != '':
                self.setText(0,'%s(%s):%s'%(self.parameter.alias, 
                                          self.parameter.type,
                                          self.parameter.value))
                self.setIcon(0,CurrentTheme.MASHUP_ALIAS_ICON)
            else:
                self.setText(0,'%s:%s'%(self.parameter.type,
                                      self.parameter.value))
                self.setIcon(0,QtGui.QIcon())
