#!/usr/bin/env python3
# coding=utf-8
"""
Used by ``make readme`` to generate github readme from rst docs.

Pipeline: routine(`include`, `out_file`)

* `include`                     - include files and inline 'heredocs'
* concatenate                   - flatten documents
* sanitize                      - modify content
    * rule__code_blocks         - - re implement rst code-blocks for github highlighting
    * rule__everything_else     - - various one liners, replace non-standard directives
* write_file(`out_file`)        -
* notify(`out_file`)            - print a progress bar
"""
from __future__ import print_function

from datetime import datetime
from functools import partial, reduce
from os.path import dirname, realpath, join, relpath


# CONFIG UTILS
# ----------------------------------------------------------------------------
def path(*args):
    return realpath(join(*args))


# CONFIG
# ----------------------------------------------------------------------------
# PATH FUNCTIONS
DOCS = partial(path, dirname(__file__))
SOURCE = partial(DOCS, 'source')
PROJECT = partial(DOCS, '..')

# CONSTANTS
README = PROJECT('README.rst')
CONTRIBUTING = PROJECT('CONTRIBUTING.rst')
TODAY = datetime.now().strftime('%A, %B %d, %Y')  # %A, %B %d, %Y -> Friday, December 11, 2015
FILE_HEADER = '.. START Source defined in %s\n\n'
FILE_FOOTER = '\n\n.. END Source defined in %s'

# SOURCE DEFINITIONS
# ----------------------------------------------------------------------------

# comment to redirect contributors
comment_line = """
.. This document was procedurally generated by %s on %s
""" % (__file__, TODAY)

# built in rst mechanic to deal with nonstandard roles
role_overrides = """
.. role:: mod(literal)
.. role:: func(literal)
.. role:: data(literal)
.. role:: const(literal)
.. role:: class(literal)
.. role:: meth(literal)
.. role:: attr(literal)
.. role:: exc(literal)
.. role:: obj(literal)
.. role:: envvar(literal)
"""


def include_readme_docs(_=None):
    yield read_text(comment_line)
    yield read_text(role_overrides)
    yield read_source('readme_title.rst')
    yield read_source('readme_features.rst')
    yield read_source('installation.rst')
    yield read_source('usage.rst')
    yield read_source('readme_credits.rst')


def include_contributing_docs(_=None):
    yield read_text(comment_line)
    yield read_text(role_overrides)
    yield read_source('contributing.rst')


# PRE COMPOSED PARTIALS
# ----------------------------------------------------------------------------
def read_source(file_name):
    return read_file(SOURCE(file_name))


# PROCESS PIPELINE
# ----------------------------------------------------------------------------
def read_file(file_name):
    yield FILE_HEADER % relpath(file_name, PROJECT())
    with open(file_name) as f:
        for line in f:
            yield line
    yield FILE_FOOTER % relpath(file_name, PROJECT())


def read_text(text):
    yield FILE_HEADER % relpath(__file__, PROJECT())
    for line in text.splitlines(True):
        yield line
    yield FILE_FOOTER % relpath(__file__, PROJECT())


def concatenate(sources):
    for source in sources:
        for line in source:
            yield line
        yield '\n'


def sanitize(lines):
    rules = rule__code_blocks, rule__everything_else
    return pipeline(rules, lines)


def write_file(file_name):
    def write_lines(lines):
        with open(file_name, 'w') as f:
            for line in lines:
                yield f.write(line)

    return write_lines


def notify(file_name):
    # Print messages for start and finish; draw a simple progress bar
    def print_notify(lines):
        print('Writing', relpath(file_name, PROJECT()), end='')
        for i, line in enumerate(lines):
            if i % 10 is 0:
                print('.', end='')
            yield line
        print('Done!')

    return print_notify


# SANITIZE RULES
# ----------------------------------------------------------------------------
def rule__code_blocks(lines):
    # Replace highlight directive with code blocks

    code_block_language = 'python'

    for line in lines:
        # named conditions
        is_new_file = line.startswith(FILE_HEADER.replace('%s', '').rstrip())
        is_code_block_shorthand = line.endswith('::\n') and not line.strip().startswith('..')

        # set highlight language and remove directive
        if line.startswith('.. highlight:: '):
            _, code_block_language = line.rstrip().rsplit(' ', 1)  # parse and set language
            continue  # remove highlight directive

        # reset highlight language to default
        if is_new_file:
            code_block_language = 'python'

        # write code block directive
        if is_code_block_shorthand:
            yield line.replace('::\n', '\n')  # remove the shorthand
            yield '\n.. code-block:: %s\n' % code_block_language  # space out new directive
            continue

        yield line


def rule__everything_else(lines):
    # add small rules here, or create a named rule.

    for line in lines:

        # remove orphan directive.
        if line.startswith(':orphan:'):
            continue

        if line.startswith('.. currentmodule::'):
            continue

        yield line


# SCRIPT UTILS
# ----------------------------------------------------------------------------
def pipeline(steps, initial=None):
    """
    Chain results from a list of functions. Inverted reduce.

    :param (function) steps: List of function callbacks
    :param initial: Starting value for pipeline.
    """

    def apply(result, step):
        return step(result)

    return reduce(apply, steps, initial)


# RUN SCRIPT
# ----------------------------------------------------------------------------
if __name__ == '__main__':
    def routine(include, out_file):
        steps = include, concatenate, sanitize, write_file(out_file), notify(out_file)
        list(pipeline(steps))


    routine(include_readme_docs, README)
    routine(include_contributing_docs, CONTRIBUTING)
