# This file is part of the Coral project.
# Please read the copyright notice in the LICENSE file.

#
# Simple QPainter-like class which produces image/svg+xml
# output. Used by the xselection mechanism
#
# BUGS:
#
# - no clipping
# - lot's of coords are integers, could be float?
# - very little tested
# - lacks many of QPainter's methods. Add as necessary
# - brush patterns and similar
# - no bezier
#

from coral.lgen import codeartifact
import re
import qt

from coral.modeler.diagram.presentation import defaultFont

class SVGPainter:
    def __init__(self):
        self.ca = codeartifact.CodeArtifact()
        self.ca.setIndentString(" ")

        self.is_active = False

        # list-of-list-of-tuples (attrname, value)
        self.saved_state = []
        # variables to save/restore
        self.saved_variables = (
            "_translate_x",
            "_translate_y",
            "_font",
            "_brush",
            "_pen",
            "_rotate_degrees",
            "_xscale",
            "_yscale",
            "_pop_g_contexts", # SPECIAL!
            )
        self._pop_g_contexts = 0

        self._translate_x = 0.0
        self._translate_y = 0.0
        self._font = defaultFont
        self._brush = None
        self._pen = None
        self._rotate_degrees = 0.0
        self._xscale = 1.0
        self._yscale = 1.0

    def begin(self, paintdevice):
        self.is_active = True
        self.ca.add('<?xml version="1.0" encoding="UTF-8"?>')
        self.ca.add('<svg font-family="Helvetica" font-size="10px" stroke="black" xml:space="preserve" fill-rule="nonzero" xmlns="http://www.w3.org/2000/svg" >')
        self.ca.indent(1)

    def end(self):
        assert self.isActive()
        # BUG this shouldn't really be happening...
        while self.saved_state:
            self.restore()

        self.__close_g_contexts()
            
        self.ca.indent(-1)
        self.ca.add("</svg>")
        self.is_active = False

    def txt(self):
        import StringIO
        s = StringIO.StringIO()
        self.ca.dump_fd(s)
        return s.getvalue()

    def isActive(self):
        return self.is_active

    def save(self):
        l = []
        for var in self.saved_variables:
            l.append((var, getattr(self, var)))
        self.saved_state.append(l)
        self._pop_g_contexts = 0

    def __close_g_contexts(self):
        for i in range(self._pop_g_contexts):
            self.ca.indent(-1)
            self.ca.add("</g>")
        
    def restore(self):
        assert self.saved_state
        lst = self.saved_state[-1]
        for var, value in lst:
            if var == "_pop_g_contexts":
                self.__close_g_contexts()
            setattr(self, var, value)
        self.saved_state.pop()

    def setClipRect(self, x, y, width, height, coordinate_mode):
        # BUG todo
        pass

    def setFont(self, font):
        if not isinstance(font, qt.QFont):
            font = qt.QFont(font)
        self._font = font

    def setBrush(self, brush):
        # BUG ignore QBrush.SolidPattern etc...
        if not isinstance(brush, qt.QBrush):
            brush = qt.QBrush(brush)

        self._brush = brush
        self.ca.add('<g fill="%s">' % (brush.color().name()))
        self.ca.indent(1)
        self._pop_g_contexts += 1

    def setPen(self, pen):
        if not isinstance(pen, qt.QPen):
            pen = qt.QPen(pen)

        if pen == self._pen:
            return

        name = pen.color().name()

        try:
            curname = self._pen.color().name()
        except AttributeError:
            curname = ""

        self._pen = pen

        dash = ""
        #if pen and pen.style() == qt.Qt.DashLine:
        #    dash = " stroke-dasharray=\"5,5\""
        self.ca.add('<g stroke="%s"%s>' % (name, dash))
        self.ca.indent(1)
        self._pop_g_contexts += 1

    def brush(self):
        return self._brush

    def pen(self):
        return self._pen

    def font(self):
        return self._font

    def fontMetrics(self):
        return qt.QFontMetrics(self._font)

    def translate(self, x, y):
        if x == 0 and y == 0:
            return
        self._translate_x += x
        self._translate_y += y
        self.ca.add('<g transform="translate(%d,%d)">' % (x, y))
        self.ca.indent(1)
        self._pop_g_contexts += 1
        
    def rotate(self, degrees):
        if degrees == 0:
            return
        self._rotate_degrees += degrees
        self.ca.add('<g transform="rotate(%d)">' % (degrees))
        self.ca.indent(1)
        self._pop_g_contexts += 1

    def scale(self, xscale, yscale):
        if xscale == 1.0 and yscale == 1.0:
            return
        self._xscale *= xscale
        self._yscale *= yscale
        self.ca.add('<g transform="scale(%f,%f)">' % (xscale, yscale))
        self.ca.indent(1)
        self._pop_g_contexts += 1

    ###### DRAWING OPERATIONS

    def drawRoundRect(self, x, y, w, h, xrnd, yrnd):
        # QT uses a different way of calculating roundness
        xrnd = xrnd * w / 100.0
        yrnd = yrnd * h / 100.0
        self.ca.add('<rect x="%d" y="%d" width="%d" height="%d" stroke-width="1" rx="%d" ry="%d"/>' % (x, y, w, h, xrnd, yrnd))

    def drawRect(self, x_or_rect, y=None, w=None, h=None):
        if isinstance(x_or_rect, qt.QRect):
            x = x_or_rect.x()
            y = x_or_rect.y()
            w = x_or_rect.width()
            h = x_or_rect.height()
        else:
            x = x_or_rect
        self.ca.add('<rect x="%d" y="%d" width="%d" height="%d" stroke-width="1" />' % (x, y, w, h))

    def drawEllipse(self, x, y, w, h):
        w2 = w / 2
        h2 = h / 2
        self.ca.add('<ellipse cx="%d" cy="%d" rx="%d" ry="%d" />' % (x + w2, y + h2, w2, h2))

    def drawPolygon(self, pointarray, winding=False, index=0, npoints=-1):
        s = []
        for i in range(pointarray.size()):
            x,y = pointarray.point(i)
            s.append(",".join([str(x),str(y)]))
        s = " ".join(s)
        self.ca.add('<polygon stroke-width="1" points="%s"/>' % (s))

    def drawPolyline(self, pointarray):
        ### This doesn't seem to work in sodipodi???
        #s = []
        #for i in range(pointarray.size()):
        #    x,y = pointarray.point(i)
        #    s.append(",".join([str(x),str(y)]))
        #s = " ".join(s)
        #self.ca.add('<polyline fill="none" stroke-width="1" points="%s"/>' % (s))
        #return

        s = ""
        for i in range(pointarray.size()):
            if i == 0:
                s += "M "
            else:
                s += "L "
            x,y = pointarray.point(i)
            s += str(x) + " " + str(y) + " "
        if not s:
            return
        #s += "z" # No, the "z" closes the polyline
        self.ca.add('<path fill="none" d="%s" />' % (s))


    def drawLine(self, x1, y1, x2, y2):
        self.ca.add('<line x1="%d" y1="%d" x2="%d" y2="%d" />' % (x1, y1, x2, y2))

    def _drawText(self, x, y, txt):
        ascent = 0
        if self._font:
            ascent = qt.QFontMetrics(self._font).ascent()

        # The color has to be re-set here because SVG
        # uses "fill" for both background and text color
        name = self._pen.color().name()
        s = '<text fill="%s" stroke="none" x="%d" y="%d">' % (name, x, y + ascent)
        # This list should be the same as xmi_write_common.cpp#escape()
        txt = re.sub("&", "&amp;", txt)
        txt = re.sub("<", "&lt;", txt)
        txt = re.sub(">", "&gt;", txt)
        txt = re.sub("\"", "&quot;", txt)
        txt = re.sub("\011", "&#9;", txt)
        txt = re.sub("\012", "&#10;", txt)
        txt = re.sub("\015", "&#13;", txt)
        s += txt + "</text>"
        self.ca.add(s)
        
    def drawText(self, x, y, w, h=0, alignmentflags=0, txt=None):
        if txt is None:
            txt = w
            y -= qt.QFontMetrics(self._font).ascent()
        self._drawText(x, y, txt)
