# coding=utf-8
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
                        unicode_literals, with_statement)

import codecs
import os
import re

from pkg_resources import resource_string
from pygments.formatters.html import HtmlFormatter
from pygments.styles import get_all_styles

from pants.backend.docgen.targets.doc import Page
from pants.base.build_environment import get_buildroot
from pants.base.exceptions import TaskError
from pants.base.generator import Generator
from pants.base.workunit import WorkUnitLabel
from pants.binaries import binary_util
from pants.build_graph.address import Address
from pants.task.task import Task
from pants.util.dirutil import safe_mkdir


def util():
  """Indirection function so we can lazy-import our utils.

  It's an expensive import that invokes re.compile a lot (via markdown and pygments),
  so we don't want to incur that cost unless we must.
  """
  from pants.backend.docgen.tasks import markdown_to_html_utils
  return markdown_to_html_utils


class MarkdownToHtml(Task):
  """Generate HTML from Markdown docs."""

  @classmethod
  def register_options(cls, register):
    register('--code-style', choices=list(get_all_styles()), default='friendly',
             fingerprint=True,
             help='Use this stylesheet for code highlights.')
    register('--open', type=bool,
             help='Open the generated documents in a browser.')
    register('--fragment', type=bool,
             fingerprint=True,
             help='Generate a fragment of html to embed in a page.')
    register('--ignore-failure', type=bool,
             fingerprint=True,
             help='Do not consider rendering errors to be build errors.')

  @classmethod
  def product_types(cls):
    return ['markdown_html', 'wiki_html']

  def __init__(self, *args, **kwargs):
    super(MarkdownToHtml, self).__init__(*args, **kwargs)
    self._templates_dir = os.path.join('templates', 'markdown')
    self.open = self.get_options().open
    self.fragment = self.get_options().fragment
    self.code_style = self.get_options().code_style

  def execute(self):
    # TODO(John Sirois): consider adding change detection

    outdir = os.path.join(self.get_options().pants_distdir, 'markdown')
    css_path = os.path.join(outdir, 'css', 'codehighlight.css')
    css = util().emit_codehighlight_css(css_path, self.code_style)
    if css:
      self.context.log.info('Emitted {}'.format(css))

    def is_page(target):
      return isinstance(target, Page)

    roots = set()
    interior_nodes = set()
    if self.open:
      dependencies_by_page = self.context.dependents(on_predicate=is_page, from_predicate=is_page)
      roots.update(dependencies_by_page.keys())
      for dependencies in dependencies_by_page.values():
        interior_nodes.update(dependencies)
        roots.difference_update(dependencies)
      for page in self.context.targets(is_page):
        # There are no in or out edges so we need to show show this isolated page.
        if not page.dependencies and page not in interior_nodes:
          roots.add(page)

    with self.context.new_workunit(name='render', labels=[WorkUnitLabel.MULTITOOL]):
      plaingenmap = self.context.products.get('markdown_html')
      wikigenmap = self.context.products.get('wiki_html')
      show = []
      for page in self.context.targets(is_page):
        def process_page(key, outdir, url_builder, genmap, fragment=False):
          if page.format == 'rst':
            with self.context.new_workunit(name='rst') as workunit:
              html_path = self.process_rst(
                workunit,
                page,
                os.path.join(outdir, util().page_to_html_path(page)),
                os.path.join(page.payload.sources.rel_path, page.source),
                self.fragment or fragment,
              )
          else:
            with self.context.new_workunit(name='md'):
              html_path = self.process_md(
                os.path.join(outdir, util().page_to_html_path(page)),
                os.path.join(page.payload.sources.rel_path, page.source),
                self.fragment or fragment,
                url_builder,
                css=css,
              )
          self.context.log.info('Processed {} to {}'.format(page.source, html_path))
          relpath = os.path.relpath(html_path, outdir)
          genmap.add(key, outdir, [relpath])
          return html_path