import platform

from Qt import QtWidgets, QtGui, QtCore

from . import model
from .awesome import tags as awesome
from .constants import (
    PluginStates, InstanceStates, PluginActionStates, Roles, EXPANDER_WIDTH
)

colors = {
    "error": QtGui.QColor("#ff4a4a"),
    "warning": QtGui.QColor("#ff9900"),
    "ok": QtGui.QColor("#77AE24"),
    "active": QtGui.QColor("#99CEEE"),
    "idle": QtCore.Qt.white,
    "inactive": QtGui.QColor("#888"),
    "hover": QtGui.QColor(255, 255, 255, 10),
    "selected": QtGui.QColor(255, 255, 255, 20),
    "outline": QtGui.QColor("#333"),
    "group": QtGui.QColor("#333"),
    "group-hover": QtGui.QColor("#3c3c3c"),
    "group-selected-hover": QtGui.QColor("#555555"),
    "expander-bg": QtGui.QColor("#222"),
    "expander-hover": QtGui.QColor("#2d6c9f"),
    "expander-selected-hover": QtGui.QColor("#3784c5")
}

scale_factors = {"darwin": 1.5}
scale_factor = scale_factors.get(platform.system().lower(), 1.0)
fonts = {
    "h3": QtGui.QFont("Open Sans", 10 * scale_factor, QtGui.QFont.Normal),
    "h4": QtGui.QFont("Open Sans", 8 * scale_factor, QtGui.QFont.Normal),
    "h5": QtGui.QFont("Open Sans", 8 * scale_factor, QtGui.QFont.DemiBold),
    "awesome6": QtGui.QFont("FontAwesome", 6 * scale_factor),
    "awesome10": QtGui.QFont("FontAwesome", 10 * scale_factor),
    "smallAwesome": QtGui.QFont("FontAwesome", 8 * scale_factor),
    "largeAwesome": QtGui.QFont("FontAwesome", 16 * scale_factor),
}
font_metrics = {
    "awesome6": QtGui.QFontMetrics(fonts["awesome6"]),
    "h4": QtGui.QFontMetrics(fonts["h4"]),
    "h5": QtGui.QFontMetrics(fonts["h5"])
}
icons = {
    "action": awesome["adn"],
    "angle-right": awesome["angle-right"],
    "angle-left": awesome["angle-left"],
    "plus-sign": awesome['plus'],
    "minus-sign": awesome['minus']
}


class PluginItemDelegate(QtWidgets.QStyledItemDelegate):
    """Generic delegate for model items"""

    def paint(self, painter, option, index):
        """Paint checkbox and text.
         _
        |_|  My label    >
        """

        body_rect = QtCore.QRectF(option.rect)

        check_rect = QtCore.QRectF(body_rect)
        check_rect.setWidth(check_rect.height())
        check_offset = (check_rect.height() / 4) + 1
        check_rect.adjust(
            check_offset, check_offset, -check_offset, -check_offset
        )

        check_color = colors["idle"]

        perspective_icon = icons["angle-right"]
        perspective_rect = QtCore.QRectF(body_rect)
        perspective_rect.setWidth(perspective_rect.height())
        perspective_rect.adjust(0, 3, 0, 0)
        perspective_rect.translate(
            body_rect.width() - (perspective_rect.width() / 2 + 2),
            0
        )

        publish_states = index.data(Roles.PublishFlagsRole)
        if publish_states & PluginStates.InProgress:
            check_color = colors["active"]

        elif publish_states & PluginStates.HasError:
            check_color = colors["error"]

        elif publish_states & PluginStates.HasWarning:
            check_color = colors["warning"]

        elif publish_states & PluginStates.WasProcessed:
            check_color = colors["ok"]

        elif not index.data(Roles.IsEnabledRole):
            check_color = colors["inactive"]

        offset = (body_rect.height() - font_metrics["h4"].height()) / 2
        label_rect = QtCore.QRectF(body_rect.adjusted(
            check_rect.width() + 12, offset - 1, 0, 0
        ))

        assert label_rect.width() > 0

        label = index.data(QtCore.Qt.DisplayRole)
        label = font_metrics["h4"].elidedText(
            label,
            QtCore.Qt.ElideRight,
            label_rect.width() - 20
        )

        font_color = colors["idle"]
        if not index.data(QtCore.Qt.CheckStateRole):
            font_color = colors["inactive"]

        # Maintain reference to state, so we can restore it once we're done
        painter.save()

        # Draw perspective icon
        painter.setFont(fonts["awesome10"])
        painter.setPen(QtGui.QPen(font_color))
        painter.drawText(perspective_rect, perspective_icon)

        # Draw label
        painter.setFont(fonts["h4"])
        painter.setPen(QtGui.QPen(font_color))
        painter.drawText(label_rect, label)

        # Draw action icon
        if index.data(Roles.PluginActionsVisibleRole):
            painter.save()
            action_state = index.data(Roles.PluginActionProgressRole)
            if action_state & PluginActionStates.HasFailed:
                color = colors["error"]
            elif action_state & PluginActionStates.HasFinished:
                color = colors["ok"]
            elif action_state & PluginActionStates.InProgress:
                color = colors["active"]
            else:
                color = colors["idle"]

            painter.setFont(fonts["smallAwesome"])
            painter.setPen(QtGui.QPen(color))

            icon_rect = QtCore.QRectF(
                option.rect.adjusted(
                    label_rect.width() - perspective_rect.width() / 2,
                    label_rect.height() / 3, 0, 0
                )
            )
            painter.drawText(icon_rect, icons["action"])

            painter.restore()

        # Draw checkbox
        pen = QtGui.QPen(check_color, 1)
        painter.setPen(pen)

        if index.data(Roles.IsOptionalRole):
            painter.drawRect(check_rect)

            if index.data(QtCore.Qt.CheckStateRole):
                optional_check_rect = QtCore.QRectF(check_rect)
                optional_check_rect.adjust(2, 2, -1, -1)
                painter.fillRect(optional_check_rect, check_color)

        else:
            painter.fillRect(check_rect, check_color)

        if option.state & QtWidgets.QStyle.State_MouseOver:
            painter.fillRect(body_rect, colors["hover"])

        if option.state & QtWidgets.QStyle.State_Selected:
            painter.fillRect(body_rect, colors["selected"])

        # Ok, we're done, tidy up.
        painter.restore()

    def sizeHint(self, option, index):
        return QtCore.QSize(option.rect.width(), 20)


class InstanceItemDelegate(QtWidgets.QStyledItemDelegate):
    """Generic delegate for model items"""

    def paint(self, painter, option, index):
        """Paint checkbox and text.
         _
        |_|  My label    >
        """

        body_rect = QtCore.QRectF(option.rect)

        check_rect = QtCore.QRectF(body_rect)
        check_rect.setWidth(check_rect.height())
        offset = (check_rect.height() / 4) + 1
        check_rect.adjust(offset, offset, -(offset), -(offset))

        check_color = colors["idle"]

        perspective_icon = icons["angle-right"]
        perspective_rect = QtCore.QRectF(body_rect)
        perspective_rect.setWidth(perspective_rect.height())
        perspective_rect.adjust(0, 3, 0, 0)
        perspective_rect.translate(
            body_rect.width() - (perspective_rect.width() / 2 + 2),
            0
        )

        publish_states = index.data(Roles.PublishFlagsRole)
        if publish_states & InstanceStates.InProgress:
            check_color = colors["active"]

        elif publish_states & InstanceStates.HasError:
            check_color = colors["error"]

        elif publish_states & InstanceStates.HasWarning:
            check_color = colors["warning"]

        elif publish_states & InstanceStates.HasFinished:
            check_color = colors["ok"]

        elif not index.data(Roles.IsEnabledRole):
            check_color = colors["inactive"]

        offset = (body_rect.height() - font_metrics["h4"].height()) / 2
        label_rect = QtCore.QRectF(body_rect.adjusted(
            check_rect.width() + 12, offset - 1, 0, 0
        ))

        assert label_rect.width() > 0

        label = index.data(QtCore.Qt.DisplayRole)
        label = font_metrics["h4"].elidedText(
            label,
            QtCore.Qt.ElideRight,
            label_rect.width() - 20
        )

        font_color = colors["idle"]
        if not index.data(QtCore.Qt.CheckStateRole):
            font_color = colors["inactive"]

        # Maintain reference to state, so we can restore it once we're done
        painter.save()

        # Draw perspective icon
        painter.setFont(fonts["awesome10"])
        painter.setPen(QtGui.QPen(font_color))
        painter.drawText(perspective_rect, perspective_icon)

        # Draw label
        painter.setFont(fonts["h4"])
        painter.setPen(QtGui.QPen(font_color))
        painter.drawText(label_rect, label)

        # Draw checkbox
        pen = QtGui.QPen(check_color, 1)
        painter.setPen(pen)

        if index.data(Roles.IsOptionalRole):
            painter.drawRect(check_rect)

            if index.data(QtCore.Qt.CheckStateRole):
                optional_check_rect = QtCore.QRectF(check_rect)
                optional_check_rect.adjust(2, 2, -1, -1)
                painter.fillRect(optional_check_rect, check_color)

        else:
            painter.fillRect(check_rect, check_color)

        if option.state & QtWidgets.QStyle.State_MouseOver:
            painter.fillRect(body_rect, colors["hover"])

        if option.state & QtWidgets.QStyle.State_Selected:
            painter.fillRect(body_rect, colors["selected"])

        # Ok, we're done, tidy up.
        painter.restore()

    def sizeHint(self, option, index):
        return QtCore.QSize(option.rect.width(), 20)


class InstanceDelegate(QtWidgets.QStyledItemDelegate):
    """Generic delegate for instance header"""

    radius = 8.0

    def __init__(self, parent):
        super(InstanceDelegate, self).__init__(parent)
        self.item_delegate = InstanceItemDelegate(parent)

    def paint(self, painter, option, index):
        if index.data(Roles.TypeRole) in (
            model.InstanceType, model.PluginType
        ):
            self.item_delegate.paint(painter, option, index)
            return

        self.group_item_paint(painter, option, index)

    def group_item_paint(self, painter, option, index):
        """Paint text
         _
        My label
        """
        body_rect = QtCore.QRectF(option.rect)
        bg_rect = QtCore.QRectF(
            body_rect.left(), body_rect.top() + 1,
            body_rect.width() - 5, body_rect.height() - 2
        )

        expander_rect = QtCore.QRectF(bg_rect)
        expander_rect.setWidth(EXPANDER_WIDTH)

        remainder_rect = QtCore.QRectF(
            expander_rect.x() + expander_rect.width(),
            expander_rect.y(),
            bg_rect.width() - expander_rect.width(),
            expander_rect.height()
        )

        width = float(expander_rect.width())
        height = float(expander_rect.height())

        x_pos = expander_rect.x()
        y_pos = expander_rect.y()

        x_radius = min(self.radius, width / 2)
        y_radius = min(self.radius, height / 2)
        x_radius2 = x_radius * 2
        y_radius2 = y_radius * 2

        expander_path = QtGui.QPainterPath()
        expander_path.moveTo(x_pos, y_pos + y_radius)
        expander_path.arcTo(
            x_pos, y_pos,
            x_radius2, y_radius2,
            180.0, -90.0
        )
        expander_path.lineTo(x_pos + width, y_pos)
        expander_path.lineTo(x_pos + width, y_pos + height)
        expander_path.lineTo(x_pos + x_radius, y_pos + height)
        expander_path.arcTo(
            x_pos, y_pos + height - y_radius2,
            x_radius2, y_radius2,
            270.0, -90.0
        )
        expander_path.closeSubpath()

        width = float(remainder_rect.width())
        height = float(remainder_rect.height())
        x_pos = remainder_rect.x()
        y_pos = remainder_rect.y()

        x_radius = min(self.radius, width / 2)
        y_radius = min(self.radius, height / 2)
        x_radius2 = x_radius * 2
        y_radius2 = y_radius * 2

        remainder_path = QtGui.QPainterPath()
        remainder_path.moveTo(x_pos + width, y_pos + height - y_radius)
        remainder_path.arcTo(
            x_pos + width - x_radius2, y_pos + height - y_radius2,
            x_radius2, y_radius2,
            0.0, -90.0
        )
        remainder_path.lineTo(x_pos, y_pos + height)
        remainder_path.lineTo(x_pos, y_pos)
        remainder_path.lineTo(x_pos + width - x_radius, y_pos)
        remainder_path.arcTo(
            x_pos + width - x_radius2, y_pos,
            x_radius2, y_radius2,
            90.0, -90.0
        )
        remainder_path.closeSubpath()

        painter.fillPath(expander_path, colors["expander-bg"])
        painter.fillPath(remainder_path, colors["group"])

        mouse_pos = option.widget.mapFromGlobal(QtGui.QCursor.pos())
        selected = option.state & QtWidgets.QStyle.State_Selected
        hovered = option.state & QtWidgets.QStyle.State_MouseOver

        if selected and hovered:
            if expander_rect.contains(mouse_pos):
                painter.fillPath(
                    expander_path, colors["expander-selected-hover"]
                )
            else:
                painter.fillPath(
                    remainder_path, colors["group-selected-hover"]
                )

        elif hovered:
            if expander_rect.contains(mouse_pos):
                painter.fillPath(expander_path, colors["expander-hover"])
            else:
                painter.fillPath(remainder_path, colors["group-hover"])

        text_height = font_metrics["awesome6"].height()
        adjust_value = (expander_rect.height() - text_height) / 2
        expander_rect.adjust(
            adjust_value + 1.5, adjust_value - 0.5,
            -adjust_value + 1.5, -adjust_value - 0.5
        )

        offset = (remainder_rect.height() - font_metrics["h5"].height()) / 2
        label_rect = QtCore.QRectF(remainder_rect.adjusted(
            5, offset - 1, 0, 0
        ))

        expander_icon = icons["plus-sign"]

        expanded = self.parent().isExpanded(index)
        if expanded:
            expander_icon = icons["minus-sign"]
        label = index.data(QtCore.Qt.DisplayRole)
        label = font_metrics["h5"].elidedText(
            label, QtCore.Qt.ElideRight, label_rect.width()
        )

        # Maintain reference to state, so we can restore it once we're done
        painter.save()

        painter.setFont(fonts["awesome6"])
        painter.setPen(QtGui.QPen(colors["idle"]))
        painter.drawText(expander_rect, QtCore.Qt.AlignCenter, expander_icon)

        # Draw label
        painter.setFont(fonts["h5"])
        painter.drawText(label_rect, label)

        # Ok, we're done, tidy up.
        painter.restore()

    def sizeHint(self, option, index):
        return QtCore.QSize(option.rect.width(), 20)


class PluginDelegate(QtWidgets.QStyledItemDelegate):
    """Generic delegate for plugin header"""

    def __init__(self, parent):
        super(PluginDelegate, self).__init__(parent)
        self.item_delegate = PluginItemDelegate(parent)

    def paint(self, painter, option, index):
        if index.data(Roles.TypeRole) in (
            model.InstanceType, model.PluginType
        ):
            self.item_delegate.paint(painter, option, index)
            return

        self.group_item_paint(painter, option, index)

    def group_item_paint(self, painter, option, index):
        """Paint text
         _
        My label
        """
        body_rect = QtCore.QRectF(option.rect)
        bg_rect = QtCore.QRectF(
            body_rect.left(), body_rect.top() + 1,
            body_rect.width() - 5, body_rect.height() - 2
        )
        radius = 8.0
        bg_path = QtGui.QPainterPath()
        bg_path.addRoundedRect(bg_rect, radius, radius)
        hovered = option.state & QtWidgets.QStyle.State_MouseOver
        selected = option.state & QtWidgets.QStyle.State_Selected
        if hovered and selected:
            painter.fillPath(bg_path, colors["group-selected-hover"])
        elif hovered:
            painter.fillPath(bg_path, colors["group-hover"])
        else:
            painter.fillPath(bg_path, colors["group"])

        expander_rect = QtCore.QRectF(bg_rect)
        expander_rect.setWidth(expander_rect.height())
        text_height = font_metrics["awesome6"].height()
        adjust_value = (expander_rect.height() - text_height) / 2
        expander_rect.adjust(
            adjust_value + 1.5, adjust_value - 0.5,
            -adjust_value + 1.5, -adjust_value - 0.5
        )

        offset = (bg_rect.height() - font_metrics["h5"].height()) / 2
        label_rect = QtCore.QRectF(bg_rect.adjusted(
            expander_rect.width() + 12, offset - 1, 0, 0
        ))

        assert label_rect.width() > 0

        expander_icon = icons["plus-sign"]

        expanded = self.parent().isExpanded(index)
        if expanded:
            expander_icon = icons["minus-sign"]
        label = index.data(QtCore.Qt.DisplayRole)
        label = font_metrics["h5"].elidedText(
            label, QtCore.Qt.ElideRight, label_rect.width()
        )

        # Maintain reference to state, so we can restore it once we're done
        painter.save()

        painter.setFont(fonts["awesome6"])
        painter.setPen(QtGui.QPen(colors["idle"]))
        painter.drawText(expander_rect, QtCore.Qt.AlignCenter, expander_icon)

        # Draw label
        painter.setFont(fonts["h5"])
        painter.drawText(label_rect, label)

        # Ok, we're done, tidy up.
        painter.restore()

    def sizeHint(self, option, index):
        return QtCore.QSize(option.rect.width(), 20)


class TerminalItem(QtWidgets.QStyledItemDelegate):
    """Delegate used exclusively for the Terminal"""

    def paint(self, painter, option, index):
        super(TerminalItem, self).paint(painter, option, index)
        item_type = index.data(Roles.TypeRole)
        if item_type == model.TerminalDetailType:
            return

        hover = QtGui.QPainterPath()
        hover.addRect(QtCore.QRectF(option.rect).adjusted(0, 0, -1, -1))
        if option.state & QtWidgets.QStyle.State_Selected:
            painter.fillPath(hover, colors["selected"])

        if option.state & QtWidgets.QStyle.State_MouseOver:
            painter.fillPath(hover, colors["hover"])
