# Define a new directive `code-block` (aliased as `sourcecode`) that uses the 
# `pygments` source highlighter to render code in color. 
#
# Incorporates code from the `Pygments`_ documentation for `Using Pygments in 
# ReST documents`_ and `Octopress`_.
#
# .. _Pygments: http://pygments.org/
# .. _Using Pygments in ReST documents: http://pygments.org/docs/rstdirective/
# .. _Octopress: http://octopress.org/

import re
import os
import md5
import __main__

# Absolute path to pygments cache dir
PYGMENTS_CACHE_DIR = os.path.abspath(os.path.join(os.path.dirname(__main__.__file__), '../../.pygments-cache'))

# Ensure cache dir exists
if not os.path.exists(PYGMENTS_CACHE_DIR):
    os.makedirs(PYGMENTS_CACHE_DIR)

from pygments.formatters import HtmlFormatter

from docutils import nodes
from docutils.parsers.rst import directives, Directive

from pygments import highlight
from pygments.lexers import get_lexer_by_name, TextLexer

class Pygments(Directive):
    """ Source code syntax hightlighting.
    """
    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    string_opts = ['title', 'url', 'caption']
    option_spec = dict([(key, directives.unchanged) for key in string_opts])
    has_content = True

    def run(self):
        self.assert_has_content()
        try:
            lexer_name = self.arguments[0]
            lexer = get_lexer_by_name(lexer_name)
        except ValueError:
            # no lexer found - use the text one instead of an exception
            lexer_name = 'text'
            lexer = TextLexer()
        formatter = HtmlFormatter()

        # Construct cache filename
        cache_file = None
        content_text = u'\n'.join(self.content)
        cache_file_name = '%s-%s.html' % (lexer_name, md5.new(content_text).hexdigest())
        cached_path = os.path.join(PYGMENTS_CACHE_DIR, cache_file_name)

        # Look for cached version, otherwise parse
        if os.path.exists(cached_path):
            cache_file = open(cached_path, 'r')
            parsed = cache_file.read()
        else:
            parsed = highlight(content_text, lexer, formatter)

        # Strip pre tag and everything outside it
        pres = re.compile("<pre>(.+)<\/pre>", re.S)
        stripped = pres.search(parsed).group(1)

        # Create tabular code with line numbers
        table = '<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers">'
        lined = ''
        for idx, line in enumerate(stripped.splitlines(True)):
            table += '<span class="line-number">%d</span>\n' % (idx + 1)
            lined  += '<span class="line">%s</span>' % line
        table += '</pre></td><td class="code"><pre><code class="%s">%s</code></pre></td></tr></table></div>' % (lexer_name, lined)

        # Add wrapper with optional caption and link
        code = '<figure class="code">'
        if self.options:
            caption = ('<span>%s</span>' % self.options['caption']) if 'caption' in self.options else ''
            title = self.options['title'] if 'title' in self.options else 'link'
            link = ('<a href="%s">%s</a>' % (self.options['url'], title)) if 'url' in self.options else ''

            if caption or link:
                code += '<figcaption>%s %s</figcaption>' % (caption, link)
        code += '%s</figure>' % table

        # Write cache
        if cache_file is None:
            cache_file = open(cached_path, 'w')
            cache_file.write(parsed)
        cache_file.close()

        return [nodes.raw('', code, format='html')]

directives.register_directive('code-block', Pygments)
directives.register_directive('sourcecode', Pygments)