# coding: utf-8

"""
This module provides a View class.

"""

import re
from types import UnboundMethodType

from .context import Context
from .loader import Loader
from .template import Template


class View(object):

    template_name = None
    template_path = None
    template = None
    template_encoding = None
    template_extension = 'mustache'

    # A function that accepts a single template_name parameter.
    _load_template = None

    def __init__(self, template=None, context=None, load_template=None, **kwargs):
        """
        Construct a View instance.

        """
        if load_template is not None:
            self._load_template = load_template

        if template is not None:
            self.template = template

        _context = Context(self)
        if context:
            _context.push(context)
        if kwargs:
            _context.push(kwargs)

        self.context = _context

    def load_template(self, template_name):
        if self._load_template is None:
            # We delay setting self._load_template until now (in the case
            # that the user did not supply a load_template to the constructor)
            # to let users set the template_extension attribute, etc. after
            # View.__init__() has already been called.
            loader = Loader(search_dirs=self.template_path, encoding=self.template_encoding,
                            extension=self.template_extension)
            self._load_template = loader.load_template

        return self._load_template(template_name)

    def get_template(self):
        """
        Return the current template after setting it, if necessary.

        """
        if not self.template:
            template_name = self._get_template_name()
            self.template = self.load_template(template_name)

        return self.template

    def _get_template_name(self):
        """
        Return the name of this Template instance.

        If the template_name attribute is not set, then this method constructs
        the template name from the class name as follows, for example:

            TemplatePartial => template_partial

        Otherwise, this method returns the template_name.

        """
        if self.template_name:
            return self.template_name

        template_name = self.__class__.__name__

        def repl(match):
            return '_' + match.group(0).lower()

        return re.sub('[A-Z]', repl, template_name)[1:]

    def render(self, encoding=None):
        """
        Return the view rendered using the current context.

        """
        template = Template(self.get_template(), self)
        return template.render(encoding=encoding)

    def get(self, key, default=None):
        return self.context.get(key, default)

    def __str__(self):
        return self.render()