# -*- coding: utf-8 -*-
"""
Pelican Comment System
======================

A Pelican plugin, which allows you to add comments to your articles.

Author: Bernhard Scheirle
"""
from __future__ import unicode_literals
import logging
import os
import copy

logger = logging.getLogger(__name__)

from itertools import chain
from pelican import signals
from pelican.readers import Readers
from pelican.writers import Writer

from . comment import Comment
from . import avatars


__version__ = "1.3.0"


_all_comments = []
_pelican_writer = None
_pelican_obj = None

def setdefault(pelican, settings):
    from pelican.settings import DEFAULT_CONFIG
    for key, value in settings:
        DEFAULT_CONFIG.setdefault(key, value)

    if not pelican:
        return

    for key, value in settings:
        pelican.settings.setdefault(key, value)


def pelican_initialized(pelican):
    from pelican.settings import DEFAULT_CONFIG
    settings = [
        ('PELICAN_COMMENT_SYSTEM', False),
        ('PELICAN_COMMENT_SYSTEM_DIR', 'comments'),
        ('PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH', 'images/identicon'),
        ('PELICAN_COMMENT_SYSTEM_IDENTICON_DATA', ()),
        ('PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE', 72),
        ('PELICAN_COMMENT_SYSTEM_AUTHORS', {}),
        ('PELICAN_COMMENT_SYSTEM_FEED', os.path.join('feeds', 'comment.%s.atom.xml')),
        ('PELICAN_COMMENT_SYSTEM_FEED_ALL', os.path.join('feeds', 'comments.all.atom.xml')),
        ('COMMENT_URL', '#comment-{slug}')
    ]

    setdefault(pelican, settings)

    DEFAULT_CONFIG['PAGE_EXCLUDES'].append(
        DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
    DEFAULT_CONFIG['ARTICLE_EXCLUDES'].append(
        DEFAULT_CONFIG['PELICAN_COMMENT_SYSTEM_DIR'])
    pelican.settings['PAGE_EXCLUDES'].append(
        pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])
    pelican.settings['ARTICLE_EXCLUDES'].append(
        pelican.settings['PELICAN_COMMENT_SYSTEM_DIR'])

    global _pelican_obj
    _pelican_obj = pelican


def initialize(article_generator):
    avatars.init(
        article_generator.settings['OUTPUT_PATH'],
        article_generator.settings[
            'PELICAN_COMMENT_SYSTEM_IDENTICON_OUTPUT_PATH'],
        article_generator.settings['PELICAN_COMMENT_SYSTEM_IDENTICON_DATA'],
        article_generator.settings[
            'PELICAN_COMMENT_SYSTEM_IDENTICON_SIZE'] / 3,
        article_generator.settings['PELICAN_COMMENT_SYSTEM_AUTHORS'],
    )

    # Reset old states (autoreload mode)
    global _all_comments
    global _pelican_writer
    _pelican_writer = _pelican_obj.get_writer()
    _all_comments = []

def warn_on_slug_collision(items):
    slugs = {}
    for comment in items:
        if not comment.slug in slugs:
            slugs[comment.slug] = [comment]
        else:
            slugs[comment.slug].append(comment)

    for slug, itemList in slugs.items():
        len_ = len(itemList)
        if len_ > 1:
            logger.warning('There are %s comments with the same slug: %s', len_, slug)
            for x in itemList:
                logger.warning('    %s', x.source_path)


def write_feed_all(gen, writer):
    if gen.settings['PELICAN_COMMENT_SYSTEM'] is not True:
        return
    if gen.settings['PELICAN_COMMENT_SYSTEM_FEED_ALL'] is None:
        return

    context = copy.copy(gen.context)
    context['SITENAME'] += " - All Comments"
    context['SITESUBTITLE'] = ""
    path = gen.settings['PELICAN_COMMENT_SYSTEM_FEED_ALL']

    global _all_comments
    _all_comments = sorted(_all_comments)
    _all_comments.reverse()

    for com in _all_comments:
        com.title = com.article.title + " - " + com.title
        com.override_url = com.article.url + com.url

    writer.write_feed(_all_comments, context, path)


def write_feed(gen, items, context, slug):
    if gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] is None:
        return

    path = gen.settings['PELICAN_COMMENT_SYSTEM_FEED'] % slug
    _pelican_writer.write_feed(items, context, path)


def process_comments(article_generator):
    for article in article_generator.articles:
        add_static_comments(article_generator, article)

def mirror_to_translations(article):
    for translation in article.translations:
        translation.comments_count = article.comments_count
        translation.comments = article.comments

def add_static_comments(gen, content):
    if gen.settings['PELICAN_COMMENT_SYSTEM'] is not True:
        return

    global _all_comments

    content.comments_count = 0
    content.comments = []
    mirror_to_translations(content)

    # Modify the local context, so we get proper values for the feed
    context = copy.copy(gen.context)
    context['SITEURL'] += "/" + content.url
    context['SITENAME'] += " - Comments: " + content.title
    context['SITESUBTITLE'] = ""

    folder = os.path.join(
        gen.settings['PATH'],
        gen.settings['PELICAN_COMMENT_SYSTEM_DIR'],
        content.slug
    )

    if not os.path.isdir(folder):
        logger.debug("No comments found for: %s", content.slug)
        write_feed(gen, [], context, content.slug)
        return

    reader = Readers(gen.settings)
    comments = []
    replies = []

    for file in os.listdir(folder):
        name, extension = os.path.splitext(file)
        if extension[1:].lower() in reader.extensions:
            com = reader.read_file(
                base_path=folder, path=file,
                content_class=Comment, context=context)

            com.article = content
            _all_comments.append(com)

            if hasattr(com, 'replyto'):
                replies.append(com)
            else:
                comments.append(com)

    feed_items = sorted(comments + replies)
    feed_items.reverse()
    warn_on_slug_collision(feed_items)

    write_feed(gen, feed_items, context, content.slug)

    # TODO: Fix this O(n²) loop
    for reply in replies:
        found_parent = False
        for comment in chain(comments, replies):
            if comment.slug == reply.replyto:
                comment.addReply(reply)
                found_parent = True
                break
        if not found_parent:
            logger.warning('Comment "%s/%s" is a reply to non-existent comment "%s". '
                'Make sure the replyto attribute is set correctly.',
                content.slug, reply.slug, reply.replyto)

    count = 0
    for comment in comments:
        comment.sortReplies()
        count += comment.countReplies()

    comments = sorted(comments)

    content.comments_count = len(comments) + count
    content.comments = comments
    mirror_to_translations(content)


def writeIdenticonsToDisk(gen, writer):
    avatars.generateAndSaveMissingAvatars()


def pelican_finalized(pelican):
    if pelican.settings['PELICAN_COMMENT_SYSTEM'] is not True:
        return
    global _all_comments
    print('Processed %s comment(s)' % len(_all_comments))


def register():
    signals.initialized.connect(pelican_initialized)
    signals.article_generator_init.connect(initialize)
    signals.article_generator_finalized.connect(process_comments)
    signals.article_writer_finalized.connect(writeIdenticonsToDisk)
    signals.article_writer_finalized.connect(write_feed_all)
    signals.finalized.connect(pelican_finalized)