"""
How to write category & categories pages using processors.

In the end, we will be creating:

* category index (view all categories)
* category folder
*   - category 1
*   - category 2
*   ....
*   - category n

1)  Build your category_index and category_detail pages.  The location of
    category_index will determine what pages it looks in.  It will search all
    pages and folders that are in the same folder (e.g.
    self.parent.pages(recursive=True) ).  The category_detail page should be in
    the same folder.

2)  The category_index should register its processor as 'category_index'
    and the category page registers as 'category_detail'.  The 'category_detail'
    processor returns no children during the build phase (they are not known),
    and the category_index node returns a plain-old PageNode along with an
    instance of CategoryFolderNode.

3)  The CategoryFolderNode generates a page for each category found in the site
    *using* the category_index page.

4)  If you want to customize category pages on a per-category, specify a
    'category' or 'categories' config in the category_detail page front matter.
"""
import os
import re

from strange_case.nodes import JinjaNode, Processor
from strange_case.registry import Registry
from strange_case.configurators import configurate
from strange_case.processors import build_node


class CategoryDetail(JinjaNode):
    """
    Gets created by the CategoryFolderProcesser during populate.
    CategoryDetail.source_paths is assigned during initial build by the
    category_detail_processor.  If the page specifies a 'category', that page
    will only bu used for that category.  If it specifies 'categories', it will
    be used for that list of categories.  So each category can have its own
    page!  StrangeCase is all about customization :-)

    The special key ``None`` refers to the default CategoryDetail template.
    """
    source_paths = {}
    index_node = None

    def __init__(self, config, source_path, target_path):
        super(CategoryDetail, self).__init__(config, source_path, target_path)
        self.count = 0
        self.pages = []


class CategoryFolderProcesser(Processor):
    def populate(self, site):
        if not CategoryDetail.source_paths:
            raise NotImplementedError('No CategoryDetail.source_paths were not assigned. '
                            'Create a node of type "category_detail"')

        pages = site.pages(recursive=True)
        categories = {}
        for page in pages:
            try:
                category = page.category
            except AttributeError:
                continue

            if category not in categories:
                config = self.config_copy()
                target_name = re.sub(r'[\W -]+', '_', category, re.UNICODE)
                config['name'] = target_name
                config['target_name'] = target_name + config['html_extension']
                config['title'] = category

                source_path = CategoryDetail.source_paths.get(category, CategoryDetail.source_paths.get(None))
                if not source_path:
                    raise NotImplementedError('No CategoryDetail page has been '
                        'registered for the "%s" category, and there is no '
                        'default page either' % category)
                configurate(source_path, config)
                categories[category] = CategoryDetail(config, source_path, self.target_folder)

            categories[category].count += 1
            categories[category].pages.append(page)

        # assign categories list to the category index page
        if CategoryDetail.index_node:
            CategoryDetail.index_node.config.setdefault('categories', categories.values())
        self.replace_with(categories.values())


def index_processor(config, source_path, target_path):
    config = configurate(source_path, config)
    categories_name = config['name']

    folder_config = config.copy()
    folder_config['target_name'] = categories_name
    folder_config['name'] = categories_name
    folder_config['type'] = config['default_folder_type']
    folder = build_node(folder_config, os.path.dirname(source_path), target_path, categories_name)[0]
    categories_folder_target_path = os.path.join(target_path, categories_name)

    config['dont_inherit'] = [key for key in config['dont_inherit'] if key != 'title']
    index_config = config.copy()
    index_config['target_name'] = config['index.html']
    index_config['name'] = 'index'
    configurate(os.path.join(source_path, config['index.html']), index_config)
    index_node = JinjaNode(index_config, source_path, categories_folder_target_path)
    folder.append(index_node)
    CategoryDetail.index_node = index_node

    # attach Processor node - this is the class that will scan the pages for
    # the `category` property:
    folder.append(CategoryFolderProcesser(config, categories_folder_target_path))
    return (folder, )


def register_category_page(category, source_path):
    if category in CategoryDetail.source_paths:
        raise TypeError('Duplicate CategoryDetail page registered for "%s"' % (category or '(default category page)'))
    CategoryDetail.source_paths[category] = source_path


def detail_processor(config, source_path, target_path):
    # just store the source path - when the detail pages get created, they
    # will use this path.
    config = configurate(source_path, config)

    if 'category' in config:
        register_category_page(config['category'], source_path)
    elif 'categories' in config:
        for category in config['categories']:
            register_category_page(category, source_path)
    else:
        register_category_page(None, source_path)
    return ()


def reset_category_detail(config):
    CategoryDetail.source_paths = {}


Registry.listen('on_start', reset_category_detail)
Registry.register('category_index', index_processor)
Registry.register('category_detail', detail_processor)
