# -*- coding: utf-8 -*-
import numpy as np

from jinja2 import Environment, PackageLoader


template_env = Environment(
    loader=PackageLoader('eli5', 'templates'),
    extensions=['jinja2.ext.with_'])
template_env.filters['render_weighted_spans'] = \
    lambda x: render_weighted_spans(x)


def format_as_html(explanation, include_styles=True, force_weights=True):
    template = template_env.get_template('explain.html')
    return template.render(
        include_styles=include_styles,
        force_weights=force_weights,
        **explanation)


def format_html_styles():
    return template_env.get_template('styles.html').render()


def render_weighted_spans(weighted_spans_data):
    """ Render text document with highlighted features.
    """
    doc = weighted_spans_data['document']
    weighted_spans = weighted_spans_data['weighted_spans']
    char_weights = np.zeros(len(doc))
    for spans, weight in weighted_spans:
        for start, end in spans:
            char_weights[start:end] += weight
    # TODO - can be much smarter, join spans at least
    # TODO - for longer documents, remove text without active features
    weight_range = max(abs(char_weights.min()), abs(char_weights.max()))
    return ''.join(
        c if np.isclose(weight, 0.) else
        '<span '
        'style="background-color: {color}" '
        'title="{weight:.3f}"'
        '>{c}</span>'.format(
            color=_weight_color(weight, weight_range),
            weight=weight,
            c=c)
        for c, weight in zip(doc, char_weights))


def _weight_color(weight, weight_range):
    """ Return css color for given weight, were the max absolute weight
    is given by weight_range.
    """
    # TODO - maybe there are better solutions for this in matplotlib
    alpha = (abs(weight) / weight_range) ** 1.5
    h, l = 255, 150
    if weight > 0:
        rgb = (l, h, l)
    else:
        rgb = (h, l, l)
    rbga = rgb + (alpha,)
    return 'rgba{}'.format(rbga)

@@ -1238,41 +1238,421 @@
     
-c if np.isclose(weight, 0.) else%0A
+_colorize(char, weight, weight_range)%0A        for char, weight in zip(doc, char_weights))%0A%0A%0Adef _colorize(char, weight, weight_range):%0A    if np.isclose(weight, 0.):%0A        return (%0A            '%3Cspan '%0A            'style=%22opacity: %7Bopacity%7D%22'%0A            '%3E%7Bchar%7D%3C/span%3E'.format(%0A                opacity=_weight_opacity(weight, weight_range),%0A                char=char)%0A        )%0A    else:%0A        return (%0A    
     
@@ -1656,32 +1656,36 @@
        '%3Cspan '%0A
+    
         'style=%22
@@ -1709,16 +1709,36 @@
  %7Bcolor%7D
+; opacity: %7Bopacity%7D
 %22 '%0A    
@@ -1741,16 +1741,20 @@
         
+   
+ 
 'title=%22
@@ -1776,20 +1776,27 @@
         
+    
 '%3E%7Bc
+har
 %7D%3C/span%3E
@@ -1817,16 +1817,20 @@
         
+    
 color=_w
@@ -1880,87 +1880,376 @@
     
-weight=weight,%0A            c=c)%0A        for c, weight in zip(doc, char
+    opacity=_weight_opacity(weight, weight_range),%0A                weight=weight,%0A                char=char)%0A        )%0A%0A%0Adef _weight_opacity(weight, weight_range):%0A    %22%22%22 Return opacity value for given weight as a string.%0A    %22%22%22%0A    min_opacity = 0.5%0A    rel_weight = abs(weight) / weight_range%0A    return '%7B:.2f%7D'.format(min_opacity + (1 - min_opacity) * rel
 _weight
-s)
 )%0A%0A%0A
@@ -2407,273 +2407,223 @@
     
-# TODO - maybe there are better solutions for this in matplotlib%0A    alpha = (abs(weight) / weight_range) ** 1.5%0A    h, l = 255, 150%0A    if weight %3E 0:%0A        rgb = (l, h, l)%0A    else:%0A        rgb = (h, l, l)%0A    rbga = rgb + (alpha,)%0A    return 'rgba%7B%7D'.format(rbg
+hue = 120 if weight %3E 0 else 0%0A    saturation = 1%0A    lightness = 1.0 - 0.5 * (abs(weight) / weight_range)%0A    alpha = 1.%0A    return 'hsla(%7B%7D, %7B:.0%25%7D, %7B:.0%25%7D, %7B:.2f%7D)'.format(%0A        hue, saturation, lightness, alph
 a)%0A

Use HSLA color space, add opacity