#
# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
#
# Jockey 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 2, or (at your option) any
# later version.
#
# Jockey 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.
#

import math

from . import basecanvas
from . import pychart_util
from .scaling import *

try:
    import zlib

    _zlib_available_p = 1
except:
    _zlib_available_p = 0


class pdf_stream(object):
    def __init__(self, fp):
        self.fp = fp
        self.off = 0

    def write(self, str):
        self.fp.write(str)
        self.off += len(str)

    def tell(self):
        return self.off


def to_radian(deg):
    return deg * 2 * math.pi / 360.0


class T(basecanvas.T):
    def __init__(self, fname, compress_p_):
        basecanvas.T.__init__(self)
        self.__out_fname = fname
        self.__reset_context()
        self.__next_obj_id = 1
        self.__next_font_id = 1
        self.__obj_offsets = {}

        # Maps font name -> integer font ID.
        self.__registered_fonts = {}

        self.__lines = []
        self.__nr_gsave = 0

        if compress_p_ and not _zlib_available_p:
            pychart_util.warn("Zlib not available. Compression request ignored.\n")
            compress_p_ = 0
        self.__compress_p = compress_p_

    def __register_font(self, name):
        "Assign an ID to the font NAME. Return its ID."
        if name not in self.__registered_fonts:
            self.__registered_fonts[name] = self.__next_font_id
            self.__next_font_id += 1
        return self.__registered_fonts[name]

    def __define_obj(self, fp, str):
        obj_id = self.__next_obj_id
        self.__next_obj_id += 1
        self.__obj_offsets[obj_id] = fp.tell()
        fp.write("%d 0 obj\n%s\nendobj\n" % (obj_id, str))
        return obj_id

    def __define_stream_obj(self, fp, s):
        if self.__compress_p:
            p = zlib.compress(s)
            return self.__define_obj(fp, "<</Length %d/Filter/FlateDecode>>\nstream\n%sendstream"
                                     % (len(p), p))
        else:
            return self.__define_obj(fp, "<</Length %d\n>>\nstream\n%s\nendstream"
                                     % (len(s), s))

    def __define_font_obj(self, fp, name, font_id):
        obj_id = self.__define_obj(fp,
                                   """<</Type/Font /Subtype/Type1 /Name/F%d /BaseFont/%s /Encoding/MacRomanEncoding>>""" % (
                                   font_id, name))
        return obj_id

    def __reset_context(self):
        self.__font_name = None
        self.__font_size = -1
        self.__line_style = None
        self.__fill_color = None
        self.__stroke_color = None
        self.__mtx_pushed = 0

    def newpath(self):
        pass

    def set_fill_color(self, color):
        if self.__fill_color == color:
            return
        if color.r == color.g and color.r == color.b:
            self.__write("%f g\n" % (color.r))
            self.__write("%f G\n" % (color.r))
        else:
            self.__write("%f %f %f rg\n" % (color.r, color.g, color.b))
            self.__write("%f %f %f RG\n" % (color.r, color.g, color.b))
        self.__fill_color = color

    def set_stroke_color(self, color):
        self.set_fill_color(color)
        return

    def __arcsub(self, x, y, radius, start, theta):
        xcos = math.cos(to_radian(theta))
        xsin = math.sin(to_radian(theta))
        x0 = radius * xcos
        y0 = radius * xsin
        x1 = radius * (4 - xcos) / 3.0
        y1 = radius * (1 - xcos) * (xcos - 3) / (3 * xsin)

        xx0, xy0 = pychart_util.rotate(x0, y0, start + theta)
        xx1, xy1 = pychart_util.rotate(x1, -y1, start + theta)
        xx2, xy2 = pychart_util.rotate(x1, y1, start + theta)
        self.__write("%f %f %f %f %f %f c\n" %
                     (x + xx1, y + xy1, x + xx2, y + xy2, x + xx0, y + xy0))

    def path_arc(self, x, y, radius, ratio, start, end):
        self.comment("PATHARC %f %f %f %f %f %f\n"
                     % (x, y, radius, ratio, start, end))
        step = 10
        if radius < 10:
            step = 20
        if radius < 5:
            step = 30
        if ratio != 1.0:
            self.push_transformation((x, y), (1, ratio), None)
            deg = start
            while deg < end:
                theta = min(step, end - deg)
                self.__arcsub(x, y, radius, deg, theta / 2)
                deg += theta
            self.pop_transformation()
        else:
            deg = start
            while deg < end:
                theta = min(step, end - deg)
                self.__arcsub(x, y, radius, deg, theta / 2)
                deg += theta
        self.comment("end PATHARC\n")

    def text_begin(self):
        self.__write("BT ")
        self.__font_name = None
        self.__font_size = None

    def text_end(self):
        self.__write("ET\n")

    def text_moveto(self, x, y, angle):
        if angle != None:
            xcos = math.cos(to_radian(angle))
            xsin = math.sin(to_radian(angle))
            self.__write("%f %f %f %f %f %f Tm " % (xcos, xsin, -xsin, xcos, x, y))
        else:
            self.__write("1 0 0 1 %f %f Tm " % (x, y))

    def text_show(self, font_name, font_size, color, str):
        if self.__font_name != font_name or self.__font_size != font_size:
            self.__write("/F%d %d Tf " % (self.__register_font(font_name), font_size))
            self.__font_name = font_name
            self.__font_size = font_size
        self.set_fill_color(color)
        self.__write("(%s) Tj " % (str.encode('mac_roman')))

    def push_transformation(self, baseloc, scale, angle, in_text=0):
        if in_text:
            op = "Tm"
        else:
            op = "cm"
            self.gsave()

        if baseloc == None:
            baseloc = (0, 0)

        if angle != None:
            radian = to_radian(angle)
            self.__write("%f %f %f %f %f %f %s\n" %
                         (math.cos(radian), math.sin(radian),
                          -math.sin(radian), math.cos(radian),
                          baseloc[0], baseloc[1], op))
        if scale != None:
            self.__write("%f 0 0 %f %f %f %s\n" % (scale[0], scale[1],
                                                   baseloc[0],
                                                   baseloc[1], op))

    def pop_transformation(self, in_text=0):
        if not in_text:
            self.grestore()

    def closepath(self):
        self.__write("h\n")

    def clip_sub(self):
        self.__write("W n\n")

    def fill(self):
        self.__write("f n\n")

    def gsave(self):
        self.__write("q\n")

    def grestore(self):
        self.__write("Q\n")
        self.__reset_context()

    def moveto(self, x, y):
        self.__write('%f %f m ' % (x, y))

    def lineto(self, x, y):
        self.__write("%f %f l\n" % (x, y))

    def stroke(self):
        self.__write("S\n")

    def set_line_style(self, style):
        if (self.__line_style == style):
            pass
        else:
            self.set_stroke_color(style.color)
            self.__write("%f w " % nscale(style.width))
            if style.dash != None:
                self.__write("[%s] 0 d\n" %
                             " ".join(map(str, nscale_seq(style.dash))))
            else:
                self.__write("[] 0 d\n")
            self.__write("%d j %d J\n" % (style.cap_style, style.join_style))
            self.__line_style = style

    def comment(self, str):
        if not self.__compress_p:
            self.__write("%%" + str + "\n")

    def verbatim(self, str):
        self.__write(str)

    def __write(self, str):
        self.__lines.append(str)

    def close(self):
        basecanvas.T.close(self)
        if self.__lines == []:
            return

        _fp, need_close = self.open_output(self.__out_fname)
        fp = pdf_stream(_fp)

        fp.write("%PDF-1.2\n")

        stream_obj_id = self.__define_stream_obj(fp, " ".join(self.__lines))

        fontstr = ""
        for font_name, font_id in list(self.__registered_fonts.items()):
            obj_id = self.__define_font_obj(fp, font_name, font_id)
            fontstr += "/F%d %d 0 R " % (font_id, obj_id)

        pages_obj_id = self.__define_obj(fp, " <</Type/Pages /Kids [%d 0 R] /Count 1 >>" % (self.__next_obj_id + 1))

        bbox = theme.adjust_bounding_box([xscale(self.__xmin), yscale(self.__ymin),
                                          xscale(self.__xmax), yscale(self.__ymax)])

        page_obj_id = self.__define_obj(fp, """  <</Type/Page
\t/Parent %d 0 R
\t/Contents %d 0 R
\t/MediaBox [%d %d %d %d]
\t/Resources << /ProcSet [/PDF /Text]
\t\t/Font << %s >>
>> >>""" % (pages_obj_id, stream_obj_id,
            bbox[0], bbox[1], bbox[2], bbox[3], fontstr))

        info_str = "/Producer (%s)\n/CreationDate (%s)" % (self.creator, self.creation_date)

        if self.title:
            info_str += "\n/Title (%s)" % (self.title,)
        if self.author:
            info_str += "\n/Author (%s)" % (self.author,)

        info_obj_id = self.__define_obj(fp, """<<%s>>""" % info_str)
        catalog_obj_id = self.__define_obj(fp, """  <</Type/Catalog/Pages %d 0 R>>""" % (pages_obj_id))

        xref_offset = fp.tell()
        fp.write("xref\n0 %d\n" % (len(self.__obj_offsets) + 1))
        fp.write("0000000000 65535 f \n")
        id = 1
        while id <= len(self.__obj_offsets):
            fp.write("%010d 00000 n \n" % (self.__obj_offsets[id]))
            id += 1
        fp.write("trailer << /Size %d /Root %d 0 R /Info %d 0 R\n>>\n" % (
        len(self.__obj_offsets) + 1, catalog_obj_id, info_obj_id))
        fp.write("startxref\n%d\n%%%%EOF\n" % xref_offset)

        if need_close:
            _fp.close()
