"""
An agg http://antigrain.com/ backend

Features that are implemented

 * capstyles and join styles
 * dashes
 * linewidth
 * lines, rectangles, ellipses
 * clipping to a rectangle
 * output to RGBA and PNG, optionally JPEG and TIFF
 * alpha blending
 * DPI scaling properly - everything scales properly (dashes, linewidths, etc)
 * draw polygon
 * freetype2 w/ ft2font

TODO:

  * integrate screen dpi w/ ppi and text

"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import six

import threading
import numpy as np
from collections import OrderedDict
from math import radians, cos, sin
from matplotlib import verbose, rcParams, __version__
from matplotlib.backend_bases import (
    _Backend, FigureCanvasBase, FigureManagerBase, RendererBase, cursors)
from matplotlib.cbook import maxdict
from matplotlib.figure import Figure
from matplotlib.font_manager import findfont, get_font
from matplotlib.ft2font import (LOAD_FORCE_AUTOHINT, LOAD_NO_HINTING,
                                LOAD_DEFAULT, LOAD_NO_AUTOHINT)
from matplotlib.mathtext import MathTextParser
from matplotlib.path import Path
from matplotlib.transforms import Bbox, BboxBase
from matplotlib import colors as mcolors

from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg
from matplotlib import _png

try:
    from PIL import Image
    _has_pil = True
except ImportError:
    _has_pil = False

backend_version = 'v2.2'

def get_hinting_flag():
    mapping = {
        True: LOAD_FORCE_AUTOHINT,
        False: LOAD_NO_HINTING,
        'either': LOAD_DEFAULT,
        'native': LOAD_NO_AUTOHINT,
        'auto': LOAD_FORCE_AUTOHINT,
        'none': LOAD_NO_HINTING
        }
    return mapping[rcParams['text.hinting']]


class RendererAgg(RendererBase):
    """
    The renderer handles all the drawing primitives using a graphics
    context instance that controls the colors/styles
    """
    debug=1

    # we want to cache the fonts at the class level so that when
    # multiple figures are created we can reuse them.  This helps with
    # a bug on windows where the creation of too many figures leads to
    # too many open file handles.  However, storing them at the class
    # level is not thread safe.  The solution here is to let the
    # FigureCanvas acquire a lock on the fontd at the start of the
    # draw, and release it when it is done.  This allows multiple
    # renderers to share the cached fonts, but only one figure can
    # draw at time and so the font cache is used by only one
    # renderer at a time

    lock = threading.RLock()
    def __init__(self, width, height, dpi):
        RendererBase.__init__(self)

        self
.dpi = dpi
        self.width = width
        self.height = height
        self._renderer = _RendererAgg(int(width), int(height), dpi, debug=False)
        self._filter_renderers = []

        self._update_methods()
        self.mathtext_parser = MathTextParser('Agg')

        self.bbox = Bbox.from_bounds(0, 0, self.width, self.height)

    def __getstate__(self):
        # We only want to 
preserve the init keywords of the Renderer.
        # Anything else can be re-created.
        return {'width': self.width, 'height': self.height, 'dpi': self.dpi}

    def __setstate__(self, state):
        self.__init__(state['width'], state['height'], state['dpi'])

    def _get_hinting_flag(self):
        if rcParams['text.hinting']:
            return LOAD_FORCE_AUTOHINT
        else:
            return LOAD_NO_HINTING

    # for filtering to work with rasterization, methods needs to be wrapped.
    # maybe there is better way to do it.
    def draw_markers(self, *kl, **kw):
        return self._renderer.draw_markers(*kl, **kw)

    def draw_path_collection(self, *kl, **kw):
        return self._renderer.draw_path_collection(*kl, **kw)

    def _update_methods(self):
        self.draw_quad_mesh = self._renderer.draw_quad_mesh
        self.draw_gouraud_triangle = self._renderer.draw_gouraud_triangle
        self.draw_gouraud_triangles = self._renderer.draw_gouraud_triangles
        self.draw_image = self._renderer.draw_image
        self.copy_from_bbox = self._renderer.copy_from_bbox
        self.get_content_extents = self._renderer.get_content_extents

    def tostring_rgba_minimized(self):
        extents = self.get_content_extents()
        bbox = [[extents[0], self.height - (extents[1] + extents[3])],
                [extents[0] + extents[2], self.height - extents[1]]]
        region = self.copy_from_bbox(bbox)
        return np.array(region), extents

    def draw_path(self, gc, path, transform, rgbFace=None):
        """
        Draw the path
        """
        nmax = rcParams['agg.path.chunksize'] # here at least for testing
        npts = path.vertices.shape[0]

        if (nmax > 100 and npts > nmax and path.should_simplify and
                rgbFace is None and gc.get_hatch() is None):
            nch = np.ceil(npts / float(nmax))
            chsize = int(np.ceil(npts / nch))
            i0 = np.arange(0, npts, chsize)
            i1 = np.zeros_like(i0)
            i1[:-1] = i0[1:] - 1
            i1[-1] = npts
            for ii0, ii1 in zip(i0, i1):
                v = path.vertices[ii0:ii1, :]
                c = path.codes
                if c is not None:
                    c = c[ii0:ii1]
                    c[0] = Path.MOVETO  # move to end of last chunk
                p = Path(v, c)
                try:
                    self._renderer.draw_path(gc, p, transform, rgbFace)
                except OverflowError:
                    raise OverflowError("Exceeded cell block limit (set 'agg.path.chunksize' rcparam)")
        else:
            try:
                self._renderer.draw_path(gc, path, transform, rgbFace)
            except OverflowError:
                raise OverflowError("Exceeded cell block limit (set 'agg.path.chunksize' rcparam)")


    def draw_mathtext(self, gc, x, y, s, prop, angle):
        """
        Draw the math text using matplotlib.mathtext
        """
        ox, oy, width, height, descent, font_image, used_characters = \
            self.mathtext_parser.parse(s, self.dpi, prop)

        xd = descent * sin(radians(angle))
        yd = descent * cos(radians(angle))
        x = np.round(x + ox + xd)
        y = np.round(y - oy + yd)
        self._renderer.draw_text_image(font_image, x, y + 1, angle, gc)

    def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
        """
        Render the text
        """
        if ismath:
            return self.draw_mathtext(gc, x, y, s, prop, angle)

        flags = get_hinting_flag()
        font = self._get_agg_font(prop)

        if font is None:
            return None
        if len(s) == 1 and ord(s) > 127:
            font.load_char(ord(s), flags=flags)
        else:
            # We pass '0' for angle here, since it will be rotated (in raster
            # space) in the following call to draw_text_image).
            font.set_text(s, 0, flags=flags)
        font.draw_glyphs_to_bitmap(antialiased=rcParams['text.antialiased'])
        d = font.get_descent() / 64.0
        # The descent needs to be adjusted for the angle
        xo, yo = font.get_bitmap_offset()
        xo /= 64.0
        yo /= 64.0
        xd = -d * sin(radians(angle))
        yd = d * cos(radians(angle))

        #print x, y, int(x), int(y), s
        self._renderer.draw_text_image(
            font, np.round(x - xd + xo), np.round(y + yd + yo) + 1, angle, gc)

    def get_text_width_height_descent(self, s, prop, ismath):
        """
        Get the width, height, and descent (offset from the bottom
        to the baseline), in display coords, of the string *s* with
        :class:`~matplotlib.font_manager.FontProperties` *prop*
        """
        if rcParams['text.usetex']:
            # todo: handle props
            size = prop.get_size_in_points()
            texmanager = self.get_texmanager()
            fontsize = prop.get_size_in_points()
            w, h, d = texmanager.get_text_width_height_descent(
                s, fontsize, renderer=self)
            return w, h, d

        if ismath:
 