# -*- coding: utf-8 -*-
# #START_LICENSE###########################################################
#
#
# This file is part of the Environment for Tree Exploration program
# (ETE).  http://ete.cgenomics.org
#  
# ETE 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 3 of the License, or
# (at your option) any later version.
#  
# ETE 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 General Public
# License for more details.
#  
# You should have received a copy of the GNU General Public License
# along with ETE.  If not, see <http://www.gnu.org/licenses/>.
#
# 
#                     ABOUT THE ETE PACKAGE
#                     =====================
# 
# ETE is distributed under the GPL copyleft license (2008-2011).  
#
# If you make use of ETE in published work, please cite:
#
# Jaime Huerta-Cepas, Joaquin Dopazo and Toni Gabaldon.
# ETE: a python Environment for Tree Exploration. Jaime BMC
# Bioinformatics 2010,:24doi:10.1186/1471-2105-11-24
#
# Note that extra references to the specific methods implemented in 
# the toolkit are available in the documentation. 
# 
# More info at http://ete.cgenomics.org
#
# 
# #END_LICENSE#############################################################
__VERSION__="ete2-2.1rev492" 
import math
import re # Used to fix SVG exporting

from PyQt4 import QtCore, QtGui, QtSvg

import qt4_circular_render as crender
import qt4_rect_render as rrender

from main import _leaf, NodeStyle, _FaceAreas
from qt4_gui import _NodeActions as _ActionDelegator
from qt4_face_render import update_node_faces, _FaceGroupItem, _TextFaceItem
import faces

## | General scheme of node content
## |==========================================================================================================================|
## |                                                fullRegion                                                                |       
## |             nodeRegion                  |================================================================================|
## |                                         |                                fullRegion                                     || 
## |                                         |        nodeRegion                     |=======================================||
## |                                         |                                       |         fullRegion                   |||
## |                                         |                                       |         nodeRegion                   ||| 
## |                                         |                         |             |branch_length | nodeSize | facesRegion|||
## |                                         | branch_length | nodesize|faces-right  |=======================================||
## |                                         |                         |(facesRegion)|=======================================||
## |                                         |                                       |             fullRegion                ||
## |                                         |                                       |             nodeRegion                ||
## |  faces-top     |          |             |                                       | branch_length | nodeSize | facesRegion||
## | branch_length  | NodeSize |faces-right  |                                       |=======================================||
## |  faces-bottom  |          |(facesRegion)|================================================================================|
## |                                         |=======================================|                                        |
## |                                         |             fullRegion                |                                        |
## |                                         |        nodeRegion                     |                                        |
## |                                         | branch_length | nodeSize | facesRegion|                                        |
## |                                         |=======================================|                                        |
## |==========================================================================================================================|

class _CircleItem(QtGui.QGraphicsEllipseItem, _ActionDelegator):
    def __init__(self, node):
        self.node = node
        d = node.img_style["size"]
        QtGui.QGraphicsEllipseItem.__init__(self, 0, 0, d, d)
        _ActionDelegator.__init__(self)

        self.setBrush(QtGui.QBrush(QtGui.QColor(self.node.img_style["fgcolor"])))
        self.setPen(QtGui.QPen(QtGui.QColor(self.node.img_style["fgcolor"])))

class _RectItem(QtGui.QGraphicsRectItem, _ActionDelegator):
    def __init__(self, node):
        self.node = node
        d = node.img_style["size"]
        QtGui.QGraphicsRectItem.__init__(self, 0, 0, d, d)
        _ActionDelegator.__init__(self)
        self.setBrush(QtGui.QBrush(QtGui.QColor(self.node.img_style["fgcolor"])))
        self.setPen(QtGui.QPen(QtGui.QColor(self.node.img_style["fgcolor"])))

class _SphereItem(QtGui.QGraphicsEllipseItem, _ActionDelegator):
    def __init__(self, node):
        self.node = node
        d = node.img_style["size"]
        r = d/2
        QtGui.QGraphicsEllipseItem.__init__(self, 0, 0, d, d)
        _ActionDelegator.__init__(self)
        #self.setBrush(QtGui.QBrush(QtGui.QColor(self.node.img_style["fgcolor"])))
        self.setPen(QtGui.QPen(QtGui.QColor(self.node.img_style["fgcolor"])))
        gradient = QtGui.QRadialGradient(r, r, r,(d)/3,(d)/3)
        gradient.setColorAt(0.05, QtCore.Qt.white);
        gradient.setColorAt(1, QtGui.QColor(self.node.img_style["fgcolor"]));
        self.setBrush(QtGui.QBrush(gradient))
        # self.setPen(QtCore.Qt.NoPen)
        
class _EmptyItem(QtGui.QGraphicsItem):
    def __init__(self, parent=None):
        QtGui.QGraphicsItem.__init__(self)
        self.setParentItem(parent)

        # qt4.6+ Only 
        try:
            self.setFlags(QtGui.QGraphicsItem.ItemHasNoContents)
        except: 
            pass

    def boundingRect(self):
        return QtCore.QRectF(0,0,0,0)

    def paint(self, *args, **kargs):
        return

class _TreeItem(QtGui.QGraphicsRectItem):
    def __init__(self, parent=None):
        QtGui.QGraphicsRectItem.__init__(self)
        self.setParentItem(parent)
        self.n2i = {}
        self.n2f = {}
    
class _NodeItem(_EmptyItem):
    def __init__(self, node, parent):
        _EmptyItem.__init__(self, parent)
        self.node = node
        self.nodeRegion = QtCore.QRectF()
        self.facesRegion = QtCore.QRectF()
        self.fullRegion = QtCore.QRectF()
        self.highlighted = False

class _LineItem(QtGui.QGraphicsLineItem):
    def paint(self, painter, option, widget):
        QtGui.QGraphicsLineItem.paint(self, painter, option, widget)

class _PointerItem(QtGui.QGraphicsRectItem):
    def __init__(self, parent=None):
        QtGui.QGraphicsRectItem.__init__(self,0,0,0,0, parent)
        self.color = QtGui.QColor("blue")
        self._active = False
        self.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))

    def paint(self, p, option, widget):
        p.setPen(self.color)
        p.drawRect(self.rect())
        return
        # Draw info text
        font = QtGui.QFont("Arial",13)
        text = "%d selected."  % len(self.get_selected_nodes())
        textR = QtGui.QFontMetrics(font).boundingRect(text)
        if  self.rect().width() > textR.width() and \
                self.rect().height() > textR.height()/2 and 0: # OJO !!!!
            p.setPen(QtGui.QPen(self.color))
            p.setFont(QtGui.QFont("Arial",13))
            p.drawText(self.rect().bottomLeft().x(),self.rect().bottomLeft().y(),text)

    def get_selected_nodes(self):
        selPath = QtGui.QPainterPath()
        selPath.addRect(self.rect())
        self.scene().setSelectionArea(selPath)
        return [i.node for i in self.scene().selectedItems()]

    def setActive(self,bool):
        self._active = bool

    def isActive(self):
        return self._active

class _TreeScene(QtGui.QGraphicsScene):
    def __init__(self):
        QtGui.QGraphicsScene.__init__(self)
        self.view = None
        self.tree = None

    def init_data(self, tree, img, n2i, n2f):
        self.master_item = _EmptyItem()
        self.view = None
        self.tree = tree
        self.n2i = n2i
        self.n2f = n2f
        self.img = img

        # Initialize scene 
        self.buffer_node = None        # Used to copy and paste
        self.pointer  = _PointerItem(self.master_item)
        self.highlighter = QtGui.QGraphicsPathItem(self.master_item)

        # Set the scene background
        self.setBackgroundBrush(QtGui.QColor("white"))
        #self.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.NoBrush))


    # def mousePressEvent(self,e):
    #     pos = self.pointer.mapFromScene(e.scenePos())
    #     self.pointer.setRect(pos.x(),pos.y(),10,10)
    #     self.pointer.startPoint = QtCore.QPointF(pos.x(), pos.y())
    #     self.pointer.setActive(True)
    #     self.pointer.setVisible(True)
    #     QtGui.QGraphicsScene.mousePressEvent(self,e)
    #  
    # def mouseReleaseEvent(self,e):
    #     curr_pos = self.pointer.mapFromScene(e.scenePos())
    #     x = min(self.pointer.startPoint.x(),curr_pos.x())
    #     y = min(self.pointer.startPoint.y(),curr_pos.y())
    #     w = max(self.pointer.startPoint.x(),curr_pos.x()) - x
    #     h = max(self.pointer.startPoint.y(),curr_pos.y()) - y
    #     if self.pointer.startPoint == curr_pos:
    #         self.pointer.setVisible(False)
    #     self.pointer.setActive(False)
    #     QtGui.QGraphicsScene.mouseReleaseEvent(self,e)
    #  
    # def mouseMoveEvent(self,e):
    #     curr_pos = self.pointer.mapFromScene(e.scenePos())
    #     if self.pointer.isActive():
    #         x = min(self.pointer.startPoint.x(),curr_pos.x())
    #         y = min(self.pointer.startPoint.y(),curr_pos.y())
    #         w = max(self.pointer.startPoint.x(),curr_pos.x()) - x
    #         h = max(self.pointer.startPoint.y(),curr_pos.y()) - y
    #         self.pointer.setRect(x,y,w,h)
    #     QtGui.QGraphicsScene.mouseMoveEvent(self, e)
    #  
    # def mouseDoubleClickEvent(self,e):
    #     QtGui.QGraphicsScene.mouseDoubleClickEvent(self,e)

    def draw(self):
        tree_item, self.n2i, self.n2f = render(self.tree, self.img)
        if self.master_item:
            self.removeItem(self.master_item)
        self.master_item = _EmptyItem()
        self.addItem(self.master_item)
        tree_item.setParentItem(self.master_item)

def render(root_node, img, hide_root=False):
    mode = img.mode
    orientation = img.orientation 

    if not img.scale and img.tree_width:
        fnode, max_dist = root_node.get_farthest_leaf(topology_only=\
                                                          img.force_topology)
        if max_dist>0:
            img.scale =  img.tree_width / max_dist
        else:
            img.scale =  1

    scale = img.scale
    arc_span = img.arc_span 
    last_rotation = img.arc_start
    layout_fn = img._layout_handler
    
    parent = _TreeItem()
    n2i = parent.n2i # node to items
    n2f = parent.n2f # node to faces

    parent.bg_layer =  _EmptyItem(parent)
    parent.tree_layer = _EmptyItem(parent)
    parent.float_layer = _EmptyItem(parent)
    parent.float_behind_layer = _EmptyItem(parent)

    TREE_LAYERS = [parent.bg_layer, parent.tree_layer, parent.float_layer]

    parent.bg_layer.setZValue(0)
    parent.tree_layer.setZValue(2)

    parent.float_behind_layer.setZValue(1)
    parent.float_layer.setZValue(3)

    visited = set()
    to_visit = []
    to_visit.append(root_node)

    # This could be used to handle aligned faces in internal
    # nodes.
    virtual_leaves = 0
    
    if img.show_branch_length:
        bl_face = faces.AttrFace("dist", fsize=8, ftype="Arial", fgcolor="black", formatter = "%0.3g")
    if img.show_branch_support:
        su_face = faces.AttrFace("support", fsize=8, ftype="Arial", fgcolor="darkred", formatter = "%0.3g")
    if img.show_leaf_name:
        na_face = faces.AttrFace("name", fsize=10, ftype="Arial", fgcolor="black")

    for n in root_node.traverse():
        set_style(n, layout_fn)

        if img.show_branch_length:
            faces.add_face_to_node(bl_face, n, 0, position="branch-top")

        if not _leaf(n) and img.show_branch_support:
            faces.add_face_to_node(su_face, n, 0, position="branch-bottom")

        if _leaf(n) and img.show_leaf_name:
            faces.add_face_to_node(na_face, n, 0, position="branch-right")

        if _leaf(n):# or len(n.img_style["_faces"]["aligned"]):
            virtual_leaves += 1

    rot_step = float(arc_span) / virtual_leaves
    #rot_step = float(arc_span) / len([n for n in root_node.traverse() if _leaf(n)])

    # ::: Precalculate values :::
    depth = 1
    while to_visit:
        node = to_visit[-1]
        finished = True
        if node not in n2i:
            # Set style according to layout function
            item = n2i[node] = _NodeItem(node, parent.tree_layer)
            item.setZValue(depth)
            depth += 1 

            if node is root_node and hide_root:
                pass
            else:
                set_node_size(node, n2i, n2f, img)

        if not _leaf(node):
            # visit children starting from left most to right
            # most. Very important!! check all children[-1] and
            # children[0]
            for c in reversed(node.children):
                if c not in visited:
                    to_visit.append(c)
                    finished = False
            # :: pre-order code here ::
        if not finished:
            continue
        else:
            to_visit.pop(-1)
            visited.add(node)

        # :: Post-order visits. Leaves are already visited here ::
        if mode == "c": 
            if _leaf(node):
                crender.init_circular_leaf_item(node, n2i, n2f, last_rotation, rot_step)
                last_rotation += rot_step
            else:
                crender.init_circular_node_item(node, n2i, n2f)

        elif mode == "r": 
            if _leaf(node):
                rrender.init_rect_leaf_item(node, n2i, n2f)
            else:
                rrender.init_rect_node_item(node, n2i, n2f)

        if node is not root_node or not hide_root: 
            render_node_content(node, n2i, n2f, img)

    mainRect = parent.rect()

    if mode == "c":
        tree_radius = crender.render_circular(root_node, n2i, rot_step)
        mainRect.adjust( -tree_radius, -tree_radius, tree_radius, tree_radius)

    else:
        iwidth = n2i[root_node].fullRegion.width()
        iheight = n2i[root_node].fullRegion.height()
        mainRect.adjust(0, 0, iwidth, iheight)
        tree_radius = iwidth

    # The order by which the following methods IS IMPORTANT

    render_floatings(n2i, n2f, img, parent.float_layer, parent.float_behind_layer)

    aligned_region_width = render_aligned_faces(img, mainRect, parent.tree_layer, n2i, n2f)
   
    render_backgrounds(img, mainRect, parent.bg_layer, n2i, n2f)

    adjust_faces_to_tranformations(img, mainRect, n2i, n2f, TREE_LAYERS)

    parent.setRect(mainRect)
    parent.setPen(QtGui.QPen(QtCore.Qt.NoPen))
    
    if img.rotation:
        rect = parent.boundingRect()
        x =  rect.x() + rect.width()/2
        y =  rect.y() +  rect.height()/2
        parent.setTransform(QtGui.QTransform().translate(x, y).rotate(img.rotation).translate(-x, -y))

    frame = QtGui.QGraphicsRectItem()
    parent.setParentItem(frame)
    mainRect = parent.mapToScene(mainRect).boundingRect()
   
    mainRect.adjust(-img.margin_left, -img.margin_top, \
                         img.margin_right, img.margin_bottom)

    add_legend(img, mainRect, frame)
    add_title(img, mainRect, frame)
    add_scale(img, mainRect, frame)
    frame.setRect(mainRect)

    # Draws a border around the tree
    if not img.show_border:
        frame.setPen(QtGui.QPen(QtCore.Qt.NoPen))
    else:
        frame.setPen(QtGui.QPen(QtGui.QColor("black")))

    return frame, n2i, n2f

def adjust_faces_to_tranformations(img, mainRect, n2i, n2f, tree_layers):
    if img.mode == "c":
        rotate_inverted_faces(n2i, n2f, img)
    elif img.mode == "r" and img.orientation == 1:
        for layer in tree_layers:
            layer.setTransform(QtGui.QTransform().translate(0, 0).scale(-1,1).translate(0, 0))
            layer.moveBy(mainRect.width(),0)
        for faceblock in n2f.itervalues():
            for pos, fb in faceblock.iteritems():
                fb.flip_hz()

def add_legend(img, mainRect, parent):
    if img.legend:
        legend = _FaceGroupItem(img.legend, None)
        legend.setup_grid()
        legend.render()
        lg_w, lg_h = legend.get_size()
        dw = max(0, lg_w-mainRect.width())
        legend.setParentItem(parent)
        if img.legend_position == 1:
            mainRect.adjust(0, -lg_h, dw, 0)
            legend.setPos(mainRect.topLeft())
        elif img.legend_position == 2:
            mainRect.adjust(0, -lg_h, dw, 0)
            pos = mainRect.topRight()
            legend.setPos(pos.x()-lg_w, pos.y())
        elif img.legend_position == 3:
            legend.setPos(mainRect.bottomLeft())
            mainRect.adjust(0, 0, dw, lg_h)
        elif img.legend_position == 4:
            pos = mainRect.bottomRight()
            legend.setPos(pos.x()-lg_w, pos.y())
            mainRect.adjust(0, 0, dw, lg_h)

def add_title(img, mainRect, parent):
    if img.title:
        title = _FaceGroupItem(img.title, None)
        title.setup_grid()
        title.render()
        lg_w, lg_h = title.get_size()
        dw = max(0, lg_w-mainRect.width())
        title.setParentItem(parent)
        mainRect.adjust(0, -lg_h, dw, 0)
        title.setPos(mainRect.topLeft())

def add_legend(img, mainRect, parent):
    if img.legend:
        legend = _FaceGroupItem(img.legend, None)
        legend.setup_grid()
        legend.render()
        lg_w, lg_h = legend.get_size()
        dw = max(0, lg_w-mainRect.width())
        legend.setParentItem(parent)
        if img.legend_position == 1:
            mainRect.adjust(0, -lg_h, dw, 0)
            legend.setPos(mainRect.topLeft())
        elif img.legend_position == 2:
            mainRect.adjust(0, -lg_h, dw, 0)
            pos = mainRect.topRight()
            legend.setPos(pos.x()-lg_w, pos.y())
        elif img.legend_position == 3:
            legend.setPos(mainRect.bottomLeft())
            mainRect.adjust(0, 0, dw, lg_h)
        elif img.legend_position == 4:
            pos = mainRect.bottomRight()
            legend.setPos(pos.x()-lg_w, pos.y())
            mainRect.adjust(0, 0, dw, lg_h)

def add_scale(img, mainRect, parent):
    if img.show_scale:
        length=50
        scaleItem = _EmptyItem()
        customPen = QtGui.QPen(QtGui.QColor("black"), 1)
        line = QtGui.QGraphicsLineItem(scaleItem)
        line2 = QtGui.QGraphicsLineItem(scaleItem)
        line3 = QtGui.QGraphicsLineItem(scaleItem)
        line.setPen(customPen)
        line2.setPen(customPen)
        line3.setPen(customPen)

        line.setLine(0, 5, length, 5)
        line2.setLine(0, 0, 0, 10)
        line3.setLine(length, 0, length, 10)
        scale_text = "%0.2f" % (float(length) / img.scale)
        scale = QtGui.QGraphicsSimpleTextItem(scale_text)
        scale.setParentItem(scaleItem)
        scale.setPos(0, 10)

        if img.force_topology:
            wtext = "Force topology is enabled!\nBranch lengths do not represent original values."
            warning_text = QtGui.QGraphicsSimpleTextItem(wtext)
            warning_text.setFont(QtGui.QFont("Arial", 8))
            warning_text.setBrush( QtGui.QBrush(QtGui.QColor("darkred")))
            warning_text.setPos(0, 32)
            warning_text.setParentItem(scaleItem)

        scaleItem.setParentItem(parent)
        dw = max(0, length-mainRect.width())
        scaleItem.setPos(mainRect.bottomLeft())
        mainRect.adjust(0,0,dw, length)    



def rotate_inverted_faces(n2i, n2f, img):
    for node, faceblock in n2f.iteritems():
        item = n2i[node]
        if item.rotation > 90 and item.rotation < 270:
            for pos, fb in faceblock.iteritems():
                fb.rotate(180)

def render_backgrounds(img, mainRect, bg_layer, n2i, n2f):

    if img.mode == "c":
        max_r = mainRect.width()/2
    else:
        max_r = mainRect.width()

    for node, item in n2i.iteritems():
        if _leaf(node):
            first_c = n2i[node]
            last_c = n2i[node]
        else:
            first_c = n2i[node.children[0]]
            last_c = n2i[node.children[-1]]

        if img.mode == "c":               
            h = item.effective_height
            angle_start = first_c.full_start 
            angle_end = last_c.full_end
            parent_radius = getattr(n2i.get(node.up, None), "radius", 0)
            base = parent_radius + item.nodeRegion.width()

            if node.img_style["node_bgcolor"].upper() != "#FFFFFF":
                bg1 = crender._ArcItem()
                r = math.sqrt(base**2 + h**2) 
                bg1.set_arc(0, 0, parent_radius, r, angle_start, angle_end)
                bg1.setParentItem(item.content.bg)
                bg1.setPen(QtGui.QPen(QtGui.QColor(node.img_style["node_bgcolor"])))
                bg1.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["node_bgcolor"])))

            if node.img_style["faces_bgcolor"].upper() != "#FFFFFF":
                bg2 = crender._ArcItem()
                r = math.sqrt(base**2 + h**2) 
                bg2.set_arc(0, 0, parent_radius, item.radius, angle_start, angle_end)
                bg2.setParentItem(item.content)
                bg2.setPen(QtGui.QPen(QtGui.QColor(node.img_style["faces_bgcolor"])))
                bg2.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["faces_bgcolor"])))

            if node.img_style["bgcolor"].upper() != "#FFFFFF":
                bg = crender._ArcItem()
                bg.set_arc(0, 0, parent_radius, max_r, angle_start, angle_end)
                bg.setPen(QtGui.QPen(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setParentItem(bg_layer)
                bg.setZValue(item.zValue())

        if img.mode == "r":
            if node.img_style["bgcolor"].upper() != "#FFFFFF":
                bg = QtGui.QGraphicsRectItem()
                pos = item.content.mapToScene(0, 0)
                bg.setPos(pos.x(), pos.y())
                bg.setRect(0, 0, max_r-pos.x(),  item.fullRegion.height())
                bg.setPen(QtGui.QPen(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setParentItem(bg_layer)
                bg.setZValue(item.zValue())

def set_node_size(node, n2i, n2f, img):
    scale = img.scale
    min_separation = img.min_leaf_separation

    item = n2i[node]
    if img.force_topology:
        branch_length = item.branch_length = float(scale)
    else:
        branch_length = item.branch_length = float(node.dist * scale)

    # Organize faces by groups
    faceblock = update_node_faces(node, n2f, img)
    aligned_height = 0
    if _leaf(node):
        if img.mode == "r": 
            aligned_height = faceblock["aligned"].h
        elif img.mode == "c":
            # aligned faces in circular mode are adjusted afterwords. The
            # min radius of the largest aligned faces will be calculated.
            pass
    
    # Total height required by the node. I cannot sum up the height of
    # all elements, since the position of some of them are forced to
    # be on top or at the bottom of branches. This fact can produce
    # and unbalanced nodeRegion center. Here, I only need to know
    # about the imbalance size to correct node height. The center will
    # be calculated later according to the parent position.
    top_half_h = ( (node.img_style["size"]/2) +
                       node.img_style["hz_line_width"]/2 +
                       faceblock["branch-top"].h )

    bottom_half_h =( (node.img_style["size"]/2) +
                       node.img_style["hz_line_width"]/2 +
                       faceblock["branch-bottom"].h )

    h1 = top_half_h + bottom_half_h
    h2 = max(faceblock["branch-right"].h, \
                 aligned_height, \
                min_separation )
    h = max(h1, h2)
    imbalance = abs(top_half_h - bottom_half_h)
    if imbalance > h2/2:
        h += imbalance - (h2/2)

    # This adds a vertical margin around the node elements
    h += img.branch_vertical_margin
    
    # Total width required by the node
    w = sum([max(branch_length + node.img_style["size"], 
                 faceblock["branch-top"].w + node.img_style["size"],
                 faceblock["branch-bottom"].w + node.img_style["size"],
                 ), 
             faceblock["branch-right"].w]
            )
    w += node.img_style["vt_line_width"]

    # rightside faces region
    item.facesRegion.setRect(0, 0, faceblock["branch-right"].w, h)

    # Node region 
    item.nodeRegion.setRect(0, 0, w, h)

    # This is the node total region covered by the node
    item.fullRegion.setRect(0, 0, w, h)

def render_node_content(node, n2i, n2f, img):
    style = node.img_style
    item = n2i[node]
    item.content = _EmptyItem(item)
 
    nodeR = item.nodeRegion
    facesR = item.facesRegion
    center = item.center
    branch_length = item.branch_length

    # Node points 
    ball_size = style["size"] 
    lw = style["vt_line_width"] 
    ball_start_x = nodeR.width() - facesR.width() - ball_size - lw

    if ball_size:
        if node.img_style["shape"] == "sphere":
            node_ball = _SphereItem(node)
        elif node.img_style["shape"] == "circle":
            node_ball = _CircleItem(node)
        elif node.img_style["shape"] == "square":
            node_ball = _RectItem(node)

        node_ball.setPos(ball_start_x, center-(ball_size/2.0))

        #from qt4_gui import _BasicNodeActions
        #node_ball.delegate = _BasicNodeActions()
        #node_ball.setAcceptsHoverEvents(True)
        #node_ball.setCursor(QtCore.Qt.PointingHandCursor)
        
    else:
        node_ball = None

    # Branch line to parent
    pen = QtGui.QPen()
    set_pen_style(pen, style["hz_line_type"])
    pen.setColor(QtGui.QColor(style["hz_line_color"]))
    pen.setWidth(style["hz_line_width"])
    pen.setCapStyle(QtCore.Qt.FlatCap)
    #pen.setCapStyle(QtCore.Qt.RoundCap)
    #pen.setJoinStyle(QtCore.Qt.RoundJoin)
    hz_line = _LineItem()
    hz_line.setPen(pen)

    # the -vt_line_width is to solve small imperfections in line
    # crosses. 
    hz_line.setLine(0, center, 
                    branch_length, center)

    if img.complete_branch_lines_when_necesary:

        extra_line = _LineItem(branch_length, center, ball_start_x, center)
        pen = QtGui.QPen()
        item.extra_branch_line = extra_line
        set_pen_style(pen, img.extra_branch_line_type)
        pen.setColor(QtGui.QColor(img.extra_branch_line_color))
        pen.setCapStyle(QtCore.Qt.FlatCap)
        pen.setWidth(style["hz_line_width"])
        extra_line.setPen(pen)
    else:
        extra_line = None

    # Attach branch-right faces to child 
    fblock_r = n2f[node]["branch-right"]
    fblock_r.render()
    fblock_r.setPos(nodeR.width() - facesR.width(), \
                        center-fblock_r.h/2)
                
    # Attach branch-bottom faces to child 
    fblock_b = n2f[node]["branch-bottom"]
    fblock_b.render()
    fblock_b.setPos(0, center + style["hz_line_width"]/2)
        
    # Attach branch-top faces to child 
    fblock_t = n2f[node]["branch-top"]
    fblock_t.render()
    fblock_t.setPos(0, center-fblock_t.h-style["hz_line_width"]/2)

    # Vertical line
    if not _leaf(node):
        if img.mode == "c":
            vt_line = QtGui.QGraphicsPathItem()

        elif img.mode == "r":
            vt_line = _LineItem(item)
            first_child_part = n2i[node.children[0]]
            last_child_part = n2i[node.children[-1]]
            c1 = first_child_part.start_y + first_child_part.center
            c2 = last_child_part.start_y + last_child_part.center
            fx = nodeR.width()-node.img_style["vt_line_width"]/2
            vt_line.setLine(fx, c1,\
                                fx, c2)            

        pen = QtGui.QPen()
        set_pen_style(pen, style["vt_line_type"])
        pen.setColor(QtGui.QColor(style["vt_line_color"]))
        pen.setWidth(style["vt_line_width"])
        pen.setCapStyle(QtCore.Qt.FlatCap)
        vt_line.setPen(pen)
        item.vt_line = vt_line
    else:
        vt_line = None

    item.bg = QtGui.QGraphicsItemGroup()
    item.movable_items = [] #QtGui.QGraphicsItemGroup()
    item.static_items = [] #QtGui.QGraphicsItemGroup()

    # Items fow which coordinates are exported in the image map
    item.mapped_items = [node_ball, fblock_r, fblock_b, fblock_t]

    for i in [node_ball, fblock_r, fblock_b, fblock_t]:
        if i:
            #item.movable_items.addToGroup(i)
            item.movable_items.append(i)
            i.setParentItem(item.content) 
            
    for i in [vt_line, extra_line, hz_line]:
        if i:
            #item.static_items.addToGroup(i)
            item.static_items.append(i)
            i.setParentItem(item.content) 

    #item.movable_items.setParentItem(item.content)
    #item.static_items.setParentItem(item.content)

def set_pen_style(pen, line_style):
    if line_style == 0:
        pen.setStyle(QtCore.Qt.SolidLine)
    elif line_style == 1:
        pen.setStyle(QtCore.Qt.DashLine)
    elif line_style == 2:
        pen.setStyle(QtCore.Qt.DotLine)

def set_style(n, layout_func):
    if not isinstance(getattr(n, "img_style", None), NodeStyle):
        n.img_style = NodeStyle()
       
    n._temp_faces = _FaceAreas()

    if layout_func:
        layout_func(n)
    
def render_floatings(n2i, n2f, img, float_layer, float_behind_layer):
    #floating_faces = [ [node, fb["float"]] for node, fb in n2f.iteritems() if "float" in fb]

    for node, faces in n2f.iteritems():
        face_set = [ [float_layer, faces.get("float", None)],
                     [float_behind_layer, faces.get("float-behind",None)]]

        for parent_layer,fb in face_set:
            if not fb:
                continue
            
            item = n2i[node]
            fb.setParentItem(parent_layer)
             
            if item.extra_branch_line:
                xtra =  item.extra_branch_line.line().dx()
            else:
                xtra = 0
             
            if img.mode == "c":
                # Floatings are positioned over branches 
                crender.rotate_and_displace(fb, item.rotation, fb.h, item.radius - item.nodeRegion.width()+ xtra)
             
                # Floatings are positioned starting from the node circle 
                #crender.rotate_and_displace(fb, item.rotation, fb.h, item.radius - item.nodeRegion.width())
             
            elif img.mode == "r":
                fb.setPos(item.content.mapToScene(0, item.center-(fb.h/2)))
             
            z = item.zValue()
            if not img.children_faces_on_top:
                z = -z
             
            fb.setZValue(z)
            fb.update_columns_size()
            fb.render()

def render_aligned_faces(img, mainRect, parent, n2i, n2f):
    # Prepares and renders aligned face headers. Used to later
    # place aligned faces
    aligned_faces = [ [node, fb["aligned"]] for node, fb in n2f.iteritems()\
                          if fb["aligned"].column2faces and _leaf(node)]

    # If no aligned faces, just return an offset of 0 pixels 
    if not aligned_faces:
        return 0

    # Load header and footer 
    if img.mode == "r":
        tree_end_x = mainRect.width()

        fb_head = _FaceGroupItem(img.aligned_header, None)
        fb_head.setParentItem(parent)
        fb_foot = _FaceGroupItem(img.aligned_foot, None)
        fb_foot.setParentItem(parent)
        surroundings = [[None,fb_foot], [None, fb_head]]
        mainRect.adjust(0, -fb_head.h, 0, fb_foot.h)
    else:
        tree_end_x = mainRect.width()/2
        surroundings = []

    # Place aligned faces and calculates the max size of each
    # column (needed to place column headers)
    c2max_w = {}
    maxh = 0
    maxh_node = None
    for node, fb in aligned_faces + surroundings:
        if fb.h > maxh:
            maxh = fb.h
            maxh_node = node 
        for c, w in fb.c2max_w.iteritems():
            c2max_w[c] = max(w, c2max_w.get(c,0))
    extra_width = sum(c2max_w.values())

    # If rect mode, render header and footer 
    if img.mode == "r": 
        if img.draw_aligned_faces_as_table:
            fb_head.setup_grid(c2max_w)
            fb_foot.setup_grid(c2max_w)

        fb_head.render()
        fb_head.setPos(tree_end_x, mainRect.top())
        fb_foot.render()
        fb_foot.setPos(tree_end_x, mainRect.bottom()-fb_foot.h)
        if img.orientation == 1:
            fb_head.flip_hz()
            fb_foot.flip_hz()

    elif img.mode == "c" and not img.allow_face_overlap:
        angle = n2i[maxh_node].angle_span
        rad, off = crender.get_min_radius(1, maxh, angle, tree_end_x)
        extra_width += rad - tree_end_x 
        tree_end_x = rad

    # Place aligned faces
    for node, fb in aligned_faces:
        item = n2i[node]
        item.mapped_items.append(fb)
        if img.draw_aligned_faces_as_table: 
            if img.aligned_table_style == 0: 
                fb.setup_grid(c2max_w, as_grid=True)
            elif img.aligned_table_style == 1: 
                fb.setup_grid(c2max_w, as_grid=False)

        fb.render()
        fb.setParentItem(item.content)
        if img.mode == "c":
            if node.up in n2i:
                x = tree_end_x - n2i[node.up].radius 
            else:
                x = tree_end_x
            #fb.moveBy(tree_end_x, 0)
        elif img.mode == "r":
            x = item.mapFromScene(tree_end_x, 0).x() 
            
        fb.setPos(x, item.center-(fb.h/2))

        if img.draw_guiding_lines and _leaf(node):
            # -1 is to connect the two lines, otherwise there is a pixel in between 
            guide_line = _LineItem(item.nodeRegion.width()-1, item.center, x, item.center)
            pen = QtGui.QPen()
            set_pen_style(pen, img.guiding_lines_type)
            pen.setColor(QtGui.QColor(img.guiding_lines_color))
            pen.setCapStyle(QtCore.Qt.FlatCap)
            pen.setWidth(node.img_style["hz_line_width"])
            guide_line.setPen(pen)
            guide_line.setParentItem(item.content)

    if img.mode == "c":
        mainRect.adjust(-extra_width, -extra_width, extra_width, extra_width)
    else:
        mainRect.adjust(0, 0, extra_width, 0)
    return extra_width

def get_tree_img_map(n2i):
    node_list = []
    face_list = []
    nid = 0
    for n, main_item in n2i.iteritems():
        n.add_feature("_nid", str(nid))
        for item in main_item.mapped_items:
            if isinstance(item, _CircleItem) \
                    or isinstance(item, _SphereItem) \
                    or isinstance(item, _RectItem):

                r = item.boundingRect()
                rect = item.mapToScene(r).boundingRect()
                x1 = rect.x() 
                y1 = rect.y() 
                x2 = rect.x() + rect.width()
                y2 = rect.y() + rect.height()
                node_list.append([x1, y1, x2, y2, nid, None])
            elif isinstance(item, _FaceGroupItem):
                if item.column2faces:
                    for f in item.childItems():
                        r = f.boundingRect()
                        rect = f.mapToScene(r).boundingRect()
                        x1 = rect.x() 
                        y1 = rect.y() 
                        x2 = rect.x() + rect.width()
                        y2 = rect.y() + rect.height()
                        if isinstance(f, _TextFaceItem):
                            face_list.append([x1, y1, x2, y2, nid, str(f.text())])
                        else:
                            face_list.append([x1, y1, x2, y2, nid, None])
                          
        nid += 1
    return {"nodes": node_list, "faces": face_list}
