# -*- coding: utf-8 -*-
from reportlab.lib.enums import TA_LEFT
from reportlab.lib.fonts import addMapping
from reportlab.lib.pagesizes import landscape, A4
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus.frames import Frame
from reportlab.platypus.paraparser import ParaFrag, ps2tt, tt2ps
from xhtml2pdf.util import getSize, getCoords, getFile, pisaFileObject, \
    getFrameDimensions
from xhtml2pdf.w3c import css
from xhtml2pdf.xhtml2pdf_reportlab import PmlPageTemplate, PmlTableOfContents, \
    PmlParagraph, PmlParagraphAndImage, PmlPageCount
import copy
import logging
import os
import re
import reportlab
import types
import urlparse
import xhtml2pdf.default
import xhtml2pdf.parser

# Copyright 2010 Dirk Holtwick, holtwick.it
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

reportlab.rl_config.warnOnMissingFontGlyphs = 0

log = logging.getLogger("xhtml2pdf")

sizeDelta = 2       # amount to reduce font size by for super and sub script
subFraction = 0.4   # fraction of font size that a sub script should be lowered
superFraction = 0.4

NBSP = u"\u00a0"

def clone(self, **kwargs):
    n = ParaFrag(**self.__dict__)
    if kwargs:
        d = n.__dict__
        d.update(kwargs)
        # This else could cause trouble in Paragraphs with images etc.
        if "cbDefn" in d:
            del d["cbDefn"]
    n.bulletText = None
    return n

ParaFrag.clone = clone

def getParaFrag(style):
    frag = ParaFrag()
    frag.sub = 0
    frag.super = 0
    frag.rise = 0
    frag.underline = 0 # XXX Need to be able to set color to fit CSS tests
    frag.strike = 0
    frag.greek = 0
    frag.link = None
    frag.text = ""

    # frag.lineBreak = 0
    #if bullet:
    #    frag.fontName, frag.bold, frag.italic = ps2tt(style.bulletFontName)
    #    frag.fontSize = style.bulletFontSize
    #    frag.textColor = hasattr(style,'bulletColor') and style.bulletColor or style.textColor
    #else:

    frag.fontName = "Times-Roman"
    frag.fontName, frag.bold, frag.italic = ps2tt(style.fontName)
    frag.fontSize = style.fontSize
    frag.textColor = style.textColor

    # Extras
    frag.leading = 0
    frag.leadingSource = "150%"
    frag.leadingSpace = 0
    frag.backColor = None
    frag.spaceBefore = 0
    frag.spaceAfter = 0
    frag.leftIndent = 0
    frag.rightIndent = 0
    frag.firstLineIndent = 0
    frag.keepWithNext = False
    frag.alignment = TA_LEFT
    frag.vAlign = None

    frag.borderWidth = 1
    frag.borderStyle = None
    frag.borderPadding = 0
    frag.borderColor = None

    frag.borderLeftWidth = frag.borderWidth
    frag.borderLeftColor = frag.borderColor
    frag.borderLeftStyle = frag.borderStyle
    frag.borderRightWidth = frag.borderWidth
    frag.borderRightColor = frag.borderColor
    frag.borderRightStyle = frag.borderStyle
    frag.borderTopWidth = frag.borderWidth
    frag.borderTopColor = frag.borderColor
    frag.borderTopStyle = frag.borderStyle
    frag.borderBottomWidth = frag.borderWidth
    frag.borderBottomColor = frag.borderColor
    frag.borderBottomStyle = frag.borderStyle

    frag.paddingLeft = 0
    frag.paddingRight = 0
    frag.paddingTop = 0
    frag.paddingBottom = 0

    frag.listStyleType = None
    frag.listStyleImage = None
    frag.whiteSpace = "normal"

    frag.wordWrap = None

    frag.pageNumber = False
    frag.pageCount = False
    frag.height = None
    frag.width = None

    frag.bulletIndent = 0
    frag.bulletText = None
    frag.bulletFontName = "Helvetica"

    frag.zoom = 1.0

    frag.outline = False
    frag.outlineLevel = 0
    frag.outlineOpen = False

    frag.insideStaticFrame = 0

    return frag

def getDirName(path):
    parts = urlparse.urlparse(path)
    if parts.scheme:
        return path
    else:
        return os.path.dirname(os.path.abspath(path))

class pisaCSSBuilder(css.CSSBuilder):

    def atFontFace(self, declarations):
        " Embed fonts "
        result = self.ruleset([self.selector('*')], declarations)
        data = result[0].values()[0]
        names = data["font-family"]

        # Font weight
        fweight = str(data.get("font-weight", "normal")).lower()
        bold = fweight in ("bold", "bolder", "500", "600", "700", "800", "900")
        if not bold and fweight <> "normal":
            log.warn(self.c.warning("@fontface, unknown value font-weight '%s'", fweight))

        # Font style
        italic = str(data.get("font-style", "")).lower() in ("italic", "oblique")

        src = self.c.getFile(data["src"])
        self.c.loadFont(
            names,
            src,
            bold=bold,
            italic=italic)
        return {}, {}

    def _pisaAddFrame(self, name, data, first=False, border=None, size=(0,0)):
        c = self.c
        if not name:
            name = "-pdf-frame-%d" % c.UID()
        x, y, w, h = getFrameDimensions(data, size[0], size[1])
        # print name, x, y, w, h
        #if not (w and h):
        #    return None
        if first:
            return (name, None, data.get("-pdf-frame-border", border), x, y, w, h, data)
        
        return (name, data.get("-pdf-frame-content", None), 
                data.get("-pdf-frame-border", border), x, y, w, h, data)

    def atPage(self, name, pseudopage, declarations):
        c = self.c
        data = {}
        name = name or "body"
        pageBorder = None

        if declarations:
            result = self.ruleset([self.selector('*')], declarations)
            # print "@PAGE", name, pseudopage, declarations, result

            if declarations:
                data = result[0].values()[0]
                pageBorder = data.get("-pdf-frame-border", None)

        if c.templateList.has_key(name):
            log.warn(self.c.warning("template '%s' has already been defined", name))

        if data.has_key("-pdf-page-size"):
            c.pageSize = xhtml2pdf.default.PML_PAGESIZES.get(str(data["-pdf-page-size"]).lower(), c.pageSize)

        isLandscape = False
        if data.has_key("size"):
            size = data["size"]
            # print size, c.pageSize
            if type(size) is not types.ListType:
                size = [size]
            sizeList = []
            for value in size:
                valueStr = str(value).lower()
                if type(value) is types.TupleType:
                    sizeList.append(getSize(value))
                elif valueStr == "landscape":
                    isLandscape = True
                elif xhtml2pdf.default.PML_PAGESIZES.has_key(valueStr):
                    c.pageSize = xhtml2pdf.default.PML_PAGESIZES[valueStr]
                else:
                    log.warn(c.warning("Unknown size value for @page"))

            if len(sizeList) == 2:
                c.pageSize = sizeList
            if isLandscape:
                c.pageSize = landscape(c.pageSize)

        for prop in ["margin-top", "margin-left", "margin-right", "margin-bottom",
                     "top", "left", "right", "bottom", "width", "height"]:
            if data.has_key(prop):
                c.frameList.append(self._pisaAddFrame(name, data, first=True, border=pageBorder, size=c.pageSize))
                break
        # self._drawing = PmlPageDrawing(self._pagesize)

        #if not c.frameList:
        #    c.warning("missing frame definitions for template")
        #    return {}, {}

        # Frames have to be calculated after we know the pagesize
        frameList = []
        staticList = []
        for fname, static, border, x, y, w, h, fdata in c.frameList:
            #fix frame sizing problem.
            if static:
                x, y, w, h = getFrameDimensions(fdata, c.pageSize[0], c.pageSize[1])
            x, y, w, h = getCoords(x, y, w, h, c.pageSize)
            if w <= 0 or h <= 0:
                log.warn(self.c.warning("Negative width or height of frame. Check @frame definitions."))
            frame = Frame(
                x, y, w, h,
                id=fname,
                leftPadding=0,
                rightPadding=0,
                bottomPadding=0,
                topPadding=0,
                showBoundary=border or pageBorder)
            if static:
                frame.pisaStaticStory = []
                c.frameStatic[static] = [frame] + c.frameStatic.get(static, [])
                staticList.append(frame)
            else:
                frameList.append(frame)

        background = data.get("background-image", None)
        if background:
            background = self.c.getFile(background)
        # print background

        # print frameList
        if not frameList:
            # print 999
            log.warn(c.warning("missing explicit frame definition for content or just static frames"))
            fname, static, border, x, y, w, h, data = self._pisaAddFrame(name, data, first=True, border=pageBorder, size=c.pageSize)
            x, y, w, h = getCoords(x, y, w, h, c.pageSize)
            if w <= 0 or h <= 0:
                log.warn(c.warning("Negative width or height of frame. Check @page definitions."))
            frameList.append(Frame(
                x, y, w, h,
                id=fname,
                leftPadding=0,
                rightPadding=0,
                bottomPadding=0,
                topPadding=0,
                showBoundary=border or pageBorder))

        pt = PmlPageTemplate(
            id=name,
            frames=frameList,
            pagesize=c.pageSize,
            )
        pt.pisaStaticList = staticList
        pt.pisaBackground = background
        pt.pisaBackgroundList = c.pisaBackgroundList

        if isLandscape:
            pt.pageorientation = pt.LANDSCAPE

        # self._pagesize)
        # pt.pml_statics = self._statics
        # pt.pml_draw = self._draw
        # pt.pml_drawing = self._drawing
        # pt.pml_background = attrs.background
        # pt.pml_bgstory = self._bgstory

        c.templateList[name] = pt
        c.template = None
        c.frameList = []
        c.frameStaticList = []

        return {}, {}

    def atFrame(self, name, declarations):
        if declarations:
            result = self.ruleset([self.selector('*')], declarations)
            # print "@BOX", name, declarations, result
            
            data = result[0]
            if data:
                data = data.values()[0]
                self.c.frameList.append(
                    self._pisaAddFrame(name, data, size=self.c.pageSize))
            
        return {}, {} # TODO: It always returns empty dicts?

class pisaCSSParser(css.CSSParser):

    def parseExternal(self, cssResourceName):

        # print "@import", self.rootPath, cssResourceName
        oldRootPath = self.rootPath
        cssFile = self.c.getFile(cssResourceName, relative=self.rootPath)
        if not cssFile:
            return None
        if self.rootPath and urlparse.urlparse(self.rootPath).scheme:
            self.rootPath = urlparse.urljoin(self.rootPath, cssResourceName)
        else:
            self.rootPath = getDirName(cssFile.uri)
        # print "###", self.rootPath
        result = self.parse(cssFile.getData())
        self.rootPath = oldRootPath
        return result

class pisaContext(object):

    """
    Helper class for creation of reportlab story and container for
    varoius data.
    """

    def __init__(self, path, debug=0, capacity=-1):
        self.fontList = copy.copy(xhtml2pdf.default.DEFAULT_FONT)
        self.path = []
        self.capacity=capacity

        self.node = None
        self.toc = PmlTableOfContents()
        self.story = []
        self.indexing_story = None
        self.text = []
        self.log = []
        self.err = 0
        self.warn = 0
        self.text = u""
        self.uidctr = 0
        self.multiBuild = False

        self.pageSize = A4
        self.template = None
        self.templateList = {}

        self.frameList = []
        self.frameStatic = {}
        self.frameStaticList = []
        self.pisaBackgroundList = []

        self.keepInFrameIndex = None

        self.baseFontSize = getSize("12pt")

        self.anchorFrag = []
        self.anchorName = []

        self.tableData = None

        self.frag = self.fragBlock = getParaFrag(ParagraphStyle('default%d' % self.UID()))
        self.fragList = []
        self.fragAnchor = []
        self.fragStack = []
        self.fragStrip = True

        self.listCounter = 0

        self.cssText = ""

        self.image = None
        self.imageData = {}
        self.force = False

        self.pathCallback = None # External callback function for path calculations

        # Store path to document
        self.pathDocument = path or "__dummy__"
        parts = urlparse.urlparse(self.pathDocument)
        if not parts.scheme:
            self.pathDocument = os.path.abspath(self.pathDocument)
        self.pathDirectory = getDirName(self.pathDocument)

        self.meta = dict(
            author="",
            title="",