# -*- coding: utf-8 -*-

"""
***************************************************************************
    ModelerGraphicItem.py
    ---------------------
    Date                 : August 2012
    Copyright            : (C) 2012 by Victor Olaya
    Email                : volayaf at gmail dot com
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************
"""

__author__ = 'Victor Olaya'
__date__ = 'August 2012'
__copyright__ = '(C) 2012, Victor Olaya'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os
from PyQt4 import QtCore, QtGui
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.parameters.Parameter import Parameter
from processing.modeler.ModelerParameterDefinitionDialog import \
        ModelerParameterDefinitionDialog
from processing.modeler.ModelerParametersDialog import ModelerParametersDialog


class ModelerGraphicItem(QtGui.QGraphicsItem):

    BOX_HEIGHT = 30
    BOX_WIDTH = 200

    def __init__(self, element, elementIndex, model):
        super(ModelerGraphicItem, self).__init__(None, None)
        self.model = model
        self.element = element
        self.elementIndex = elementIndex
        self.inputFolded = True
        self.outputFolded = True
        if isinstance(element, Parameter):
            icon = QtGui.QIcon(os.path.dirname(__file__)
                               + '/../images/input.png')
            self.pixmap = icon.pixmap(20, 20, state=QtGui.QIcon.On)
            self.text = element.description
        elif isinstance(element, basestring):
            # Output name
            icon = QtGui.QIcon(os.path.dirname(__file__)
                               + '/../images/output.png')
            self.pixmap = icon.pixmap(20, 20, state=QtGui.QIcon.On)
            self.text = element
        else:
            state = QtGui.QIcon.On
            if self.elementIndex in self.model.deactivated:
                state = QtGui.QIcon.Off
            self.text = element.name
            self.pixmap = element.getIcon().pixmap(15, 15, state=state)
        self.arrows = []
        self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
        self.setZValue(1000)

        if not isinstance(element, basestring):
            icon = QtGui.QIcon(os.path.dirname(__file__)
                               + '/../images/edit.png')
            pt = QtCore.QPointF(ModelerGraphicItem.BOX_WIDTH / 2
                                - FlatButtonGraphicItem.WIDTH / 2,
                                ModelerGraphicItem.BOX_HEIGHT / 2
                                - FlatButtonGraphicItem.HEIGHT / 2 + 1)
            self.editButton = FlatButtonGraphicItem(icon, pt, self.editElement)
            self.editButton.setParentItem(self)
            icon = QtGui.QIcon(os.path.dirname(__file__)
                               + '/../images/delete.png')
            pt = QtCore.QPointF(ModelerGraphicItem.BOX_WIDTH / 2
                                - FlatButtonGraphicItem.WIDTH / 2,
                                -ModelerGraphicItem.BOX_HEIGHT / 2
                                + FlatButtonGraphicItem.HEIGHT / 2 + 1)
            self.deleteButton = FlatButtonGraphicItem(icon, pt,
                    self.removeElement)
            self.deleteButton.setParentItem(self)

        if isinstance(element, GeoAlgorithm):
            if element.parameters:
                pt = self.getLinkPointForParameter(-1)
                x = self.getXPositionForFoldButton()
                pt = QtCore.QPointF(x, pt.y() + 2)
                self.inButton = FoldButtonGraphicItem(pt, self.foldInput)
                self.inButton.setParentItem(self)
            if element.outputs:
                pt = self.getLinkPointForOutput(-1)
                x = self.getXPositionForFoldButton()
                pt = QtCore.QPointF(x, pt.y() + 2)
                self.outButton = FoldButtonGraphicItem(pt, self.foldOutput)
                self.outButton.setParentItem(self)

    def foldInput(self, folded):
        self.inputFolded = folded
        self.prepareGeometryChange()
        if self.element.outputs:
            pt = self.getLinkPointForOutput(-1)
            x = self.getXPositionForFoldButton()
            pt = QtCore.QPointF(x, pt.y())
            self.outButton.position = pt
        self.update()

    def foldOutput(self, folded):
        self.outputFolded = folded
        self.prepareGeometryChange()
        self.update()

    def addArrow(self, arrow):
        self.arrows.append(arrow)

    def boundingRect(self):
        font = QtGui.QFont('Verdana', 8)
        fm = QtGui.QFontMetricsF(font)
        numParams = (0 if self.inputFolded else len(self.element.parameters))
        numOutputs = (0 if self.outputFolded else len(self.element.outputs))
        hUp = fm.height() * 1.2 * (numParams + 2)
        hDown = fm.height() * 1.2 * (numOutputs + 2)
        rect = QtCore.QRectF(-(ModelerGraphicItem.BOX_WIDTH + 2) / 2,
                             -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2 - hUp,
                             ModelerGraphicItem.BOX_WIDTH + 2,
                             ModelerGraphicItem.BOX_HEIGHT + hDown + hUp)
        return rect

    def mouseDoubleClickEvent(self, event):
        self.editElement()

    def contextMenuEvent(self, event):
        popupmenu = QtGui.QMenu()
        removeAction = popupmenu.addAction('Remove')
        removeAction.triggered.connect(self.removeElement)
        editAction = popupmenu.addAction('Edit')
        editAction.triggered.connect(self.editElement)
        if isinstance(self.element, GeoAlgorithm):
            if self.elementIndex in self.model.deactivated:
                removeAction = popupmenu.addAction('Activate')
                removeAction.triggered.connect(self.activateAlgorithm)
            else:
                deactivateAction = popupmenu.addAction('Deactivate')
                deactivateAction.triggered.connect(self.deactivateAlgorithm)
        popupmenu.exec_(event.screenPos())

    def deactivateAlgorithm(self):
        self.model.setPositions(self.scene().getParameterPositions(),
                                self.scene().getAlgorithmPositions(),
                                self.scene().getOutputPositions())
        self.model.deactivateAlgorithm(self.elementIndex, True)

    def activateAlgorithm(self):
        self.model.setPositions(self.scene().getParameterPositions(),
                                self.scene().getAlgorithmPositions(),
                                self.scene().getOutputPositions())
        if not self.model.activateAlgorithm(self.elementIndex, True):
            QtGui.QMessageBox.warning(None, 'Could not activate Algorithm',
                    'The selected algorithm depends on other currently \
                    non-active algorithms.\nActivate them them before trying \
                    to activate it.')

    def editElement(self):
        self.model.setPositions(self.scene().getParameterPositions(),
                                self.scene().getAlgorithmPositions(),
                                self.scene().getOutputPositions())
        if isinstance(self.element, Parameter):
            dlg = ModelerParameterDefinitionDialog(self.model,
                    param=self.element)
            dlg.exec_()
            if dlg.param is not None:
                self.model.updateParameter(self.elementIndex, dlg.param)
                self.element = dlg.param
                self.text = self.element.description
                self.update()
        elif isinstance(self.element, GeoAlgorithm):
            dlg = self.element.getCustomModelerParametersDialog(self.model,
                    self.elementIndex)
            if not dlg:
                dlg = ModelerParametersDialog(self.element, self.model,
                        self.elementIndex)
            dlg.exec_()
            if dlg.params is not None:
                self.model.updateAlgorithm(self.elementIndex, dlg.params,
                        dlg.values, dlg.outputs, dlg.dependencies)

    def removeElement(self):
        if isinstance(self.element, Parameter):
            if not self.model.removeParameter(self.elementIndex):
                QtGui.QMessageBox.warning(None, 'Could not remove element',
                        'Other elements depend on the selected one.\nRemove \
                        them before trying to remove it.')
        elif isinstance(self.element, GeoAlgorithm):
            if not self.model.removeAlgorithm(self.elementIndex):
                QtGui.QMessageBox.warning(None, 'Could not remove element',
                        'Other elements depend on the selected one.\nRemove \
                        them before trying to remove it.')

    def getAdjustedText(self, text):
        font = QtGui.QFont('Verdana', 8)
        fm = QtGui.QFontMetricsF(font)
        w = fm.width(text)
        if w < self.BOX_WIDTH - 25 - FlatButtonGraphicItem.WIDTH:
            return text

        text = text[0:-3] + '...'
        w = fm.width(text)
        while w > self.BOX_WIDTH - 25 - FlatButtonGraphicItem.WIDTH:
            text = text[0:-4] + '...'
            w = fm.width(text)
        return text

    def paint(self, painter, option, widget=None):
        rect = QtCore.QRectF(-(ModelerGraphicItem.BOX_WIDTH + 2) / 2.0,
                             -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2.0,
                             ModelerGraphicItem.BOX_WIDTH + 2,
                             ModelerGraphicItem.BOX_HEIGHT + 2)
        painter.setPen(QtGui.QPen(QtCore.Qt.gray, 1))
        color = QtGui.QColor(125, 232, 232)
        if isinstance(self.element, Parameter):
            color = QtGui.QColor(179, 179, 255)
        elif isinstance(self.element, GeoAlgorithm):
            color = QtCore.Qt.white
        painter.setBrush(QtGui.QBrush(color, QtCore.Qt.SolidPattern))
        painter.drawRect(rect)
        font = QtGui.QFont('Verdana', 8)
        painter.setFont(font)
        painter.setPen(QtGui.QPen(QtCore.Qt.black))
        if self.isSelected():
            painter.setPen(QtGui.QPen(QtCore.Qt.blue))
        if isinstance(self.element, GeoAlgorithm):
            if self.elementIndex in self.model.deactivated:
                painter.setPen(QtGui.QPen(QtCore.Qt.lightGray))
        fm = QtGui.QFontMetricsF(font)
        text = self.getAdjustedText(self.text)
        h = fm.height()
        pt = QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2 + 25, h / 2.0)
        painter.drawText(pt, text)
        painter.setPen(QtGui.QPen(QtCore.Qt.black))
        if isinstance(self.element, GeoAlgorithm):
            h = -(fm.height() * 1.2)
            h = h - ModelerGraphicItem.BOX_HEIGHT / 2.0 + 5
            pt = QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2 + 25, h)
            painter.drawText(pt, 'In')
            i = 1
            if not self.inputFolded:
                for param in self.element.parameters:
                    text = self.getAdjustedText(param.description)
                    h = -(fm.height() * 1.2) * (i + 1)
                    h = h - ModelerGraphicItem.BOX_HEIGHT / 2.0 + 5
                    pt = QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2
                            + 33, h)
                    painter.drawText(pt, text)
                    i += 1
            i = 1
            h = fm.height() * 1.2
            h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
            pt = QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2 + 25, h)
            painter.drawText(pt, 'Out')
            if not self.outputFolded:
                for out in self.element.outputs:
                    text = self.getAdjustedText(out.description)
                    h = fm.height() * 1.2 * (i + 1)
                    h = h + ModelerGraphicItem.BOX_HEIGHT / 2.0
                    pt = QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2
                            + 33, h)
                    painter.drawText(pt, text)
                    i += 1
        if self.pixmap:
            painter.drawPixmap(-(ModelerGraphicItem.BOX_WIDTH / 2.0) + 3, -8,
                               self.pixmap)

    def getLinkPointForParameter(self, paramIndex):
        offsetX = 25
        if self.inputFolded:
            paramIndex = -1
            offsetX = 17
        font = QtGui.QFont('Verdana', 8)
        fm = QtGui.QFontMetricsF(font)
        if isinstance(self.element, GeoAlgorithm):
            h = -(fm.height() * 1.2) * (paramIndex + 2) - fm.height() / 2.0 + 8
            h = h - ModelerGraphicItem.BOX_HEIGHT / 2.0
        else:
            h = 0
        return QtCore.QPointF(-ModelerGraphicItem.BOX_WIDTH / 2 + offsetX, h)

    def getXPositionForFoldButton(self):
        return 0

    def getLinkPointForOutput(self, outputIndex):
        if isinstance(self.element, GeoAlgorithm):
            numParams = 0
            outputIndex = (outputIndex if not self.outputFolded else -1)
            text = self.getAdjustedText(
                    self.element.outputs[outputIndex].description)
            font = QtGui.QFont('Verdana', 8)
            fm = QtGui.QFontMetricsF(font)
            w = fm.width(text)
            h = fm.height() * 1.2 * (outputIndex + 1) + fm.height() / 2.0
            y = h + ModelerGraphicItem.BOX_HEIGHT / 2.0 + 5
            x = (-ModelerGraphicItem.BOX_WIDTH / 2 + 33 + w
                 + 5 if not self.outputFolded else 10)
            return QtCore.QPointF(x, y)
        else:
            return QtCore.QPointF(0, 0)

    def itemChange(self, change, value):
        if change == QtGui.QGraphicsItem.ItemPositionChange:
            for arrow in self.arrows:
                arrow.updatePosition()

        return value

    def polygon(self):
        font = QtGui.QFont('Verdana', 8)
        fm = QtGui.QFontMetricsF(font)
        hUp = fm.height() * 1.2 * (len(self.element.parameters) + 2)
        hDown = fm.height() * 1.2 * (len(self.element.outputs) + 2)
        pol = QtGui.QPolygonF([
                QtCore.QPointF(
                        -(ModelerGraphicItem.BOX_WIDTH + 2) / 2,
                        -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2 - hUp),
                QtCore.QPointF(
                        -(ModelerGraphicItem.BOX_WIDTH + 2) / 2,
                        (ModelerGraphicItem.BOX_HEIGHT + 2) / 2 + hDown),
                QtCore.QPointF(
                        (ModelerGraphicItem.BOX_WIDTH + 2) / 2,
                        (ModelerGraphicItem.BOX_HEIGHT + 2) / 2 + hDown),
                QtCore.QPointF(
                        (ModelerGraphicItem.BOX_WIDTH + 2) / 2,
                        -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2 - hUp),
                QtCore.QPointF(
                        -(ModelerGraphicItem.BOX_WIDTH + 2) / 2,
                        -(ModelerGraphicItem.BOX_HEIGHT + 2) / 2 - hUp)])
        return pol


class FlatButtonGraphicItem(QtGui.QGraphicsItem):

    WIDTH = 16
    HEIGHT = 16

    def __init__(self, icon, position, action):
        super(FlatButtonGraphicItem, self).__init__(None, None)
        self.setAcceptHoverEvents(True)
        self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, False)
        self.pixmap = icon.pixmap(self.WIDTH, self.HEIGHT,
                                  state=QtGui.QIcon.On)
        self.position = position
        self.isIn = False
        self.action = action

    def mousePressEvent(self, event):
        self.action()

    def paint(self, painter, option, widget=None):
        pt = QtCore.QPointF(-self.WIDTH / 2, -self.HEIGHT / 2) + self.position
        rect = QtCore.QRectF(pt.x(), pt.y(), self.WIDTH, self.HEIGHT)
        if self.isIn:
            painter.setPen(QtGui.QPen(QtCore.Qt.transparent, 1))
            painter.setBrush(QtGui.QBrush(QtCore.Qt.lightGray,
                             QtCore.Qt.SolidPattern))
        else:
            painter.setPen(QtGui.QPen(QtCore.Qt.transparent, 1))
            painter.setBrush(QtGui.QBrush(QtCore.Qt.transparent,
                             QtCore.Qt.SolidPattern))
        painter.drawRect(rect)
        painter.drawPixmap(pt.x(), pt.y(), self.pixmap)

    def boundingRect(self):
        rect = QtCore.QRectF(self.position.x() - self.WIDTH / 2,
                             self.position.y() - self.HEIGHT / 2, self.WIDTH,
                             self.HEIGHT)
        return rect

    def hoverEnterEvent(self, event):
        self.isIn = True
        self.update()

    def hoverLeaveEvent(self, event):
        self.isIn = False
        self.update()


class FoldButtonGraphicItem(FlatButtonGraphicItem):

    WIDTH = 11
    HEIGHT = 11

    icons = {True: QtGui.QIcon(os.path.dirname(__file__)
             + '/../images/plus.png'),
             False: QtGui.QIcon(os.path.dirname(__file__)
             + '/../images/minus.png')}

    def __init__(self, position, action):
        self.folded = True
        icon = self.icons[True]
        super(FoldButtonGraphicItem, self).__init__(icon, position, action)

    def mousePressEvent(self, event):
        self.folded = not self.folded
        icon = self.icons[self.folded]
        self.pixmap = icon.pixmap(self.WIDTH, self.HEIGHT,
                                  state=QtGui.QIcon.On)
        self.action(self.folded)