# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals

import locale
import logging
import os
import re
import subprocess
import sys
import unittest
from contextlib import contextmanager
from functools import wraps
from logging.handlers import BufferingHandler
from os.path import abspath, dirname, join
from shutil import rmtree
from tempfile import mkdtemp

from six import StringIO

from pelican.contents import Article
from pelican.settings import (DEFAULT_CONFIG, read_settings)

__all__ = ['get_article', 'unittest', ]


@contextmanager
def temporary_folder():
    """creates a temporary folder, return it and delete it afterwards.

    This allows to do something like this in tests:

        >>> with temporary_folder() as d:
            # do whatever you want
    """
    tempdir = mkdtemp()
    try:
        yield tempdir
    finally:
        rmtree(tempdir)


def isplit(s, sep=None):
    """Behaves like str.split but returns a generator instead of a list.

    >>> list(isplit('\tUse the force\n')) == '\tUse the force\n'.split()
    True
    >>> list(isplit('\tUse the force\n')) == ['Use', 'the', 'force']
    True
    >>> (list(isplit('\tUse the force\n', "e"))
         == '\tUse the force\n'.split("e"))
    True
    >>> list(isplit('Use the force', "e")) == 'Use the force'.split("e")
    True
    >>> list(isplit('Use the force', "e")) == ['Us', ' th', ' forc', '']
    True

    """
    sep, hardsep = r'\s+' if sep is None else re.escape(sep), sep is not None
    exp, pos, length = re.compile(sep), 0, len(s)
    while True:
        m = exp.search(s, pos)
        if not m:
            if pos < length or hardsep:
                #      ^ mimic "split()": ''.split() returns []
                yield s[pos:]
            break
        start = m.start()
        if pos < start or hardsep:
            #           ^ mimic "split()": includes trailing empty string
            yield s[pos:start]
        pos = m.end()


def mute(returns_output=False):
    """Decorate a function that prints to stdout, intercepting the output.
    If "returns_output" is True, the function will return a generator
    yielding the printed lines instead of the return values.

    The decorator literally hijack sys.stdout during each function
    execution, so be careful with what you apply it to.

    >>> def numbers():
        print "42"
        print "1984"
    ...
    >>> numbers()
    42
    1984
    >>> mute()(numbers)()
    >>> list(mute(True)(numbers)())
    ['42', '1984']

    """

    def decorator(func):

        @wraps(func)
        def wrapper(*args, **kwargs):

            saved_stdout = sys.stdout
            sys.stdout = StringIO()

            try:
                out = func(*args, **kwargs)
                if returns_output:
                    out = isplit(sys.stdout.getvalue().strip())
            finally:
                sys.stdout = saved_stdout

            return out

        return wrapper

    return decorator


def get_article(title, slug, content, lang, extra_metadata=None):
    metadata = {'slug': slug, 'title': title, 'lang': lang}
    if extra_metadata is not None:
        metadata.update(extra_metadata)
    return Article(content, metadata=metadata)


def skipIfNoExecutable(executable):
    """Skip test if `executable` is not found

    Tries to run `executable` with subprocess to make sure it's in the path,
    and skips the tests if not found (if subprocess raises a `OSError`).
    """

    with open(os.devnull, 'w') as fnull:
        try:
            res = subprocess.call(executable, stdout=fnull, stderr=fnull)
        except OSError:
            res = None

    if res is None:
        return unittest.skip('{0} executable not found'.format(executable))

    return lambda func: func


def module_exists(module_name):
    """Test if a module is importable."""

    try:
        __import__(module_name)
    except ImportError:
        return False
    else:
        return True


def locale_available(locale_):
    old_locale = locale.setlocale(locale.LC_TIME)

    try:
        locale.setlocale(locale.LC_TIME, str(locale_))
    except locale.Error:
        return False
    else:
        locale.setlocale(locale.LC_TIME, old_locale)
        return True


def get_settings(**kwargs):
    """Provide tweaked setting dictionaries for testing

    Set keyword arguments to override specific settings.
    """
    settings = DEFAULT_CONFIG.copy()
    for key, value in kwargs.items():
        settings[key] = value
    return settings

def get_my_settings(**kwargs):
  PATH = abspath(dirname(__file__))
  default_conf = join(PATH, 'default_conf.py')
  settings = read_settings(default_conf)
  for key, value in kwargs.items():
    settings[key] = value
  return settings


class LogCountHandler(BufferingHandler):
    """Capturing and counting logged messages."""

    def __init__(self, capacity=1000):
        logging.handlers.BufferingHandler.__init__(self, capacity)

    def count_logs(self, msg=None, level=None):
        return len([
            l
            for l
            in self.buffer
            if (msg is None or re.match(msg, l.getMessage())) and
               (level is None or l.levelno == level)
        ])


class LoggedTestCase(unittest.TestCase):
    """A test case that captures log messages."""

    def setUp(self):
        super(LoggedTestCase, self).setUp()
        self._logcount_handler = LogCountHandler()
        logging.getLogger().addHandler(self._logcount_handler)

    def tearDown(self):
        logging.getLogger().removeHandler(self._logcount_handler)
        super(LoggedTestCase, self).tearDown()

    def assertLogCountEqual(self, count=None, msg=None, **kwargs):
        actual = self._logcount_handler.count_logs(msg=msg, **kwargs)
        self.assertEqual(
            actual, count,
            msg='expected {} occurrences of {!r}, but found {}'.format(
                count, msg, actual))

from bs4 import BeautifulSoup
from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator,
                                PelicanTemplateNotFound, StaticGenerator,
                                TemplatePagesGenerator)
from pelican.readers import RstReader
from pelican.writers import Writer
from pelican.contents import (Article, Page)

CUR_DIR = os.path.dirname(__file__)
CONTENT_DIR = os.path.join(CUR_DIR, 'content')
OUTPUT_DIR = os.path.join(CUR_DIR, 'output')

class BaseTest(object):

  def __init__(self):
    self.initSettings()

  def initSettings(self):
    self.old_locale = locale.setlocale(locale.LC_ALL)
    locale.setlocale(locale.LC_ALL, str('C'))
    self.settings = get_my_settings()
    self.settings['THEME'] = "../"
    self.settings['filenames'] = {}
    self.reader = RstReader(self.settings)
    self.writer = Writer("output", self.settings)

  def gen_article_and_html_from_rst(self, rstPath):
    content, metadata = self.reader.read(rstPath)
    article = Article(content=content, metadata=metadata)
    generator = ArticlesGenerator( context=self.settings.copy(), settings=self.settings, path=CONTENT_DIR, theme=self.settings['THEME'], output_path=OUTPUT_DIR)
    generator.generate_context()
    f = lambda a: True if (a.slug == article.slug) else False
    result = filter(f, generator.context["articles"])[0]
    self.writer.write_file(
                result.save_as, generator.get_template('article'),
                generator.context, article=result)
    soup = BeautifulSoup(open("./"+self.writer.output_path+'/'+result.save_as), "html.parser")
    return (result, soup)

  def gen_page_and_html_from_rst(self, rstPath):
    content, metadata = self.reader.read(rstPath)
    page = Page(content=content, metadata=metadata)
    generator = PagesGenerator( context=self.settings.copy(), settings=self.settings, path=CONTENT_DIR, theme=self.settings['THEME'], output_path=OUTPUT_DIR)
    generator.generate_context()
    f = lambda a: True if (a.slug == page.slug) else False
    result = filter(f, generator.context["pages"])[0]
    self.writer.write_file(
                result.save_as, generator.get_template('page'),
                generator.context, page=result)
    soup = BeautifulSoup(open("./"+self.writer.output_path+'/'+result.save_as), "html.parser")
    return (result, soup)

  def gen_tag_and_html_from_name(self, name):
    generator = ArticlesGenerator( context=self.settings.copy(), settings=self.settings, path=CONTENT_DIR, theme=self.settings['THEME'], output_path=OUTPUT_DIR)
    generator.generate_context()
    generator.generate_tags(self.writer.write_file)
    selectedTag = None

    for tag, articles in generator.tags.items():
      if tag.name == name:
        selectedTag = tag

    soup = BeautifulSoup(open("./"+self.writer.output_path+'/'+selectedTag.save_as), "html.parser")
    return (selectedTag, soup)

  def gen_category_and_html_from_name(self, name):
    generator = ArticlesGenerator( context=self.settings.copy(), settings=self.settings, path=CONTENT_DIR, theme=self.settings['THEME'], output_path=OUTPUT_DIR)
    generator.generate_context()
    generator.generate_categories(self.writer.write_file)
    selectedCategory = None

    for category, articles in generator.categories:
      if category.name == name:
        selectedCategory = category

    soup = BeautifulSoup(open("./"+self.writer.output_path+'/'+selectedCategory.save_as), "html.parser")
    return (selectedCategory, soup)

  def gen_author_and_html_from_name(self, name):
    generator = ArticlesGenerator( context=self.settings.copy(), settings=self.settings, path=CONTENT_DIR, theme=self.settings['THEME'], output_path=OUTPUT_DIR)
    generator.generate_context()
    generator.generate_authors(self.writer.write_file)
    selectedAuthor = None

    for author, articles in generator.authors:
      if author.name == name:
        selectedAuthor = author

    soup = BeautifulSoup(open("./"+self.writer.output_path+'/'+selectedAuthor.save_as), "html.parser")
    return (selectedAuthor, soup)