from reportlab.lib import colors
from reportlab.lib.colors import black, white
from reportlab.graphics.shapes import Polygon, String, Drawing, Group, Rect
from reportlab.graphics.widgetbase import Widget
from reportlab.lib.attrmap import *
from reportlab.lib.validators import *
from reportlab.lib.units import cm
from reportlab.pdfbase.pdfmetrics import getFont
from reportlab.graphics.widgets.grids import ShadedRect

class SlideBox(Widget):
    """Returns a slidebox widget"""
    _attrMap = AttrMap(
        labelFontName = AttrMapValue(isString, desc="Name of font used for the labels"),
        labelFontSize = AttrMapValue(isNumber, desc="Size of font used for the labels"),
        labelStrokeColor = AttrMapValue(isColorOrNone, desc="Colour for for number outlines"),
        labelFillColor = AttrMapValue(isColorOrNone, desc="Colour for number insides"),
        startColor = AttrMapValue(isColor, desc='Color of first box'),
        endColor = AttrMapValue(isColor, desc='Color of last box'),
        numberOfBoxes = AttrMapValue(isInt, desc='How many boxes there are'),
        trianglePosition = AttrMapValue(isInt, desc='Which box is highlighted by the triangles'),
        triangleHeight = AttrMapValue(isNumber, desc="Height of indicator triangles"),
        triangleWidth = AttrMapValue(isNumber, desc="Width of indicator triangles"),
        triangleFillColor = AttrMapValue(isColor, desc="Colour of indicator triangles"),
        triangleStrokeColor = AttrMapValue(isColorOrNone, desc="Colour of indicator triangle outline"),
        triangleStrokeWidth = AttrMapValue(isNumber, desc="Colour of indicator triangle outline"),
        boxHeight = AttrMapValue(isNumber, desc="Height of the boxes"),
        boxWidth = AttrMapValue(isNumber, desc="Width of the boxes"),
        boxSpacing = AttrMapValue(isNumber, desc="Space between the boxes"),
        boxOutlineColor = AttrMapValue(isColorOrNone, desc="Colour used to outline the boxes (if any)"),
        boxOutlineWidth = AttrMapValue(isNumberOrNone, desc="Width of the box outline (if any)"),
        leftPadding = AttrMapValue(isNumber, desc='Padding on left of drawing'),
        rightPadding = AttrMapValue(isNumber, desc='Padding on right of drawing'),
        topPadding = AttrMapValue(isNumber, desc='Padding at top of drawing'),
        bottomPadding = AttrMapValue(isNumber, desc='Padding at bottom of drawing'),
        background = AttrMapValue(isColorOrNone, desc='Colour of the background to the drawing (if any)'),
        sourceLabelText = AttrMapValue(isNoneOrString, desc="Text used for the 'source' label (can be empty)"),
        sourceLabelOffset = AttrMapValue(isNumber, desc='Padding at bottom of drawing'),
        sourceLabelFontName = AttrMapValue(isString, desc="Name of font used for the 'source' label"),
        sourceLabelFontSize = AttrMapValue(isNumber, desc="Font size for the 'source' label"),
        sourceLabelFillColor = AttrMapValue(isColorOrNone, desc="Colour ink for the 'source' label (bottom right)"),
        )

    def __init__(self):
        self.labelFontName = "Helvetica-Bold"
        self.labelFontSize = 10
        self.labelStrokeColor = black
        self.labelFillColor = white
        self.startColor = colors.Color(232/255.0,224/255.0,119/255.0)
        self.endColor = colors.Color(25/255.0,77/255.0,135/255.0)
        self.numberOfBoxes = 7
        self.trianglePosition = 7
        self.triangleHeight = 0.12*cm
        self.triangleWidth = 0.38*cm
        self.triangleFillColor = white
        self.triangleStrokeColor = black
        self.triangleStrokeWidth = 0.58
        self.boxHeight = 0.55*cm
        self.boxWidth = 0.73*cm
        self.boxSpacing = 0.075*cm
        self.boxOutlineColor = black
        self.boxOutlineWidth = 0.58
        self.leftPadding=5
        self.rightPadding=5
        self.topPadding=5
        self.bottomPadding=5
        self.background=None
        self.sourceLabelText = "Source: ReportLab"
        self.sourceLabelOffset = 0.2*cm
        self.sourceLabelFontName = "Helvetica-Oblique"
        self.sourceLabelFontSize = 6
        self.sourceLabelFillColor = black

    def _getDrawingDimensions(self):
        tx=(self.numberOfBoxes*self.boxWidth)
        if self.numberOfBoxes>1: tx=tx+((self.numberOfBoxes-1)*self.boxSpacing)
        tx=tx+self.leftPadding+self.rightPadding
        ty=self.boxHeight+self.triangleHeight
        ty=ty+self.topPadding+self.bottomPadding+self.sourceLabelOffset+self.sourceLabelFontSize
        return (tx,ty)

    def _getColors(self):
        # for calculating intermediate colors...
        numShades = self.numberOfBoxes+1
        fillColorStart = self.startColor
        fillColorEnd = self.endColor
        colorsList =[]

        for i in range(0,numShades):
            colorsList.append(colors.linearlyInterpolatedColor(fillColorStart, fillColorEnd, 0, numShades-1, i))
        return colorsList

    def demo(self,drawing=None):
        if not drawing:
            tx,ty=self._getDrawingDimensions()
            drawing = Drawing(tx,ty)
        drawing.add(self.draw())
        return drawing

    def draw(self):
        g = Group()
        ys = self.bottomPadding+(self.triangleHeight/2)+self.sourceLabelOffset+self.sourceLabelFontSize
        if self.background:
            x,y = self._getDrawingDimensions()
            g.add(Rect(-self.leftPadding,-ys,x,y,
                       strokeColor=None,
                       strokeWidth=0,
                       fillColor=self.background))

        ascent=getFont(self.labelFontName).face.ascent/1000.
        if ascent==0: ascent=0.718 # default (from helvetica)
        ascent=ascent*self.labelFontSize # normalize

        colorsList = self._getColors()

        # Draw the boxes - now uses ShadedRect from grids
        x=0
        for f in range (0,self.numberOfBoxes):
            sr=ShadedRect()
            sr.x=x
            sr.y=0
            sr.width=self.boxWidth
            sr.height=self.boxHeight
            sr.orientation = 'vertical'
            sr.numShades = 30
            sr.fillColorStart = colorsList[f]
            sr.fillColorEnd = colorsList[f+1]
            sr.strokeColor = None
            sr.strokeWidth = 0

            g.add(sr)

            g.add(Rect(x,0,self.boxWidth,self.boxHeight,
                   strokeColor=self.boxOutlineColor,
                   strokeWidth=self.boxOutlineWidth,
                   fillColor=None))

            g.add(String(x+self.boxWidth/2.,(self.boxHeight-ascent)/2.,
                   text = str(f+1),
                   fillColor = self.labelFillColor,
                   strokeColor=self.labelStrokeColor,
                   textAnchor = 'middle',
                   fontName = self.labelFontName,
                   fontSize = self.labelFontSize))
            x=x+self.boxWidth+self.boxSpacing

        #do triangles
        xt = (self.trianglePosition*self.boxWidth)
        if self.trianglePosition>1:
            xt = xt+(self.trianglePosition-1)*self.boxSpacing
        xt = xt-(self.boxWidth/2)
        g.add(Polygon(
            strokeColor = self.triangleStrokeColor,
            strokeWidth = self.triangleStrokeWidth,
            fillColor = self.triangleFillColor,
            points=[xt,self.boxHeight-(self.triangleHeight/2),
                    xt-(self.triangleWidth/2),self.boxHeight+(self.triangleHeight/2),
                    xt+(self.triangleWidth/2),self.boxHeight+(self.triangleHeight/2),
                        xt,self.boxHeight-(self.triangleHeight/2)]))
        g.add(Polygon(
            strokeColor = self.triangleStrokeColor,
            strokeWidth = self.triangleStrokeWidth,
            fillColor = self.triangleFillColor,
            points=[xt,0+(self.triangleHeight/2),
                    xt-(self.triangleWidth/2),0-(self.triangleHeight/2),
                    xt+(self.triangleWidth/2),0-(self.triangleHeight/2),
                    xt,0+(self.triangleHeight/2)]))

        #source label
        if self.sourceLabelText != None:
            g.add(String(x-self.boxSpacing,0-(self.triangleHeight/2)-self.sourceLabelOffset-(self.sourceLabelFontSize),
                       text = self.sourceLabelText,
                       fillColor = self.sourceLabelFillColor,
                       textAnchor = 'end',
                       fontName = self.sourceLabelFontName,
                       fontSize = self.sourceLabelFontSize))

        g.shift(self.leftPadding, ys)

        return g


if __name__ == "__main__":
    d = SlideBox()
    d.demo().save(fnRoot="slidebox")
