# -*- coding: utf-8 -*-
"""
# Clay.core

Main file

"""
from __future__ import absolute_import

import glob
import mimetypes
from os.path import (isfile, isdir, realpath, abspath, normpath, dirname,
    join, splitext, exists, sep)
import socket
import sys

from jinja2 import (PackageLoader, ChoiceLoader, FileSystemLoader)
from jinja2.exceptions import TemplateSyntaxError
from shake import (Shake, Settings, Render, Rule, NotFound, send_file,
    Response)

from . import utils as u, config as c
from . import p_scss, p_less, p_markdown, p_coffee
from . import pp_pygments


class Clay(object):

    def __init__(self, base_dir, settings=None, source_dir=c.SOURCE_DIR):
        base_dir = normpath(abspath(realpath(base_dir)))
        if not isdir(base_dir):
            base_dir = dirname(base_dir)
        self.base_dir = u.to_unicode(base_dir)
        self.source_dir = u.make_dirs(base_dir, source_dir)
        self.build_dir = join(base_dir, c.BUILD_DIR)
 
        settings = settings or {}
        self.settings = Settings(settings, c.default_settings)

        theme_prefix = self.settings.get('theme_prefix', '').strip('/')
        if theme_prefix:
            theme_prefix += '/'
        self.settings['theme_prefix'] = theme_prefix

        views_ignore = self.settings.get('views_ignore', [])
        self.settings['views_ignore'] = tuple(views_ignore)

        self.app = Shake(__file__, c.app_settings)
        self._make_render()
        self._enable_pre_processors()
        self._add_urls()

    def _make_render(self):
        loader = ChoiceLoader([
            FileSystemLoader(self.source_dir),
            PackageLoader('clay', c.SOURCE_DIR),
        ])
        self.render = Render(loader=loader)
        self.render.env.filters['json'] = u.filter_to_json

    def _enable_pre_processors(self):
        ext_trans = {}
        processors = self.settings.pre_processors

        for name in processors:
            pr = globals().get('p_' + name)
            if pr and pr.enabled:
                pr.add_extensions(self)
                for ext in pr.extensions_in:
                    ext_trans[ext] = pr.extension_out

        self.ext_trans = ext_trans

    def _add_urls(self):
        self.app.add_urls([
            Rule('/', self.render_view),
            Rule('/<path:path>', self.render_view),
        ])

    def _normalize_path(self, path):
        if '..' in path:
            return self.not_found()
        path = path.strip('/')
        is_dir = isdir(join(self.source_dir, path))
        if is_dir:
            path += u'/'
        if not path or is_dir:
            path += 'index.html'
        return path

    def _translate_ext(self, old_ext):
        return self.ext_trans.get(old_ext, old_ext)

    def _get_alternative(self, path):
        path = path.strip('/')
        fname, ext = splitext(path)
        fullpath = join(c.DEFAULT_TEMPLATES, path)
        if exists(fullpath):
            return ext, path, fullpath
        
        if path != 'index.html':
            return None, None, None

        pdir = join(self.source_dir, fname + '.*')
        files = glob.glob(pdir)
        if files:
            fullpath = files[0]
            path = fullpath.replace(self.source_dir, '')
            _, ext = splitext(path)
            return ext, path, fullpath

        return None, None, None

    def _post_process(self, html):
        html = u.to_unicode(html)
        processors = self.settings.post_processors

        for name in processors:
            pp = globals().get('pp_' + name)
            if pp and pp.enabled:
                html = pp.process(html)

        return html

    def run(self, host=None, port=None):
        host = host if host is not None else self.settings.HOST
        port = port if port is not None else self.settings.PORT
        try:
            port = int(port)
        except Exception:
            port = self.settings.PORT
        ips = [ip for ip in socket.gethostbyname_ex(socket.gethostname())[2]
            if not ip.startswith("127.")][:1]
        if ips:
            print ' * Your local IP is:', ips[0]
        try:
            self.app.run(host=host, port=port)
        except socket.error, e:
            print e

    def build(self):
        """Generates a static version of the site.
        """
        print '\nGenerating views...\n', '-' * 20
        self.build_views()
        print '\nDone!\n'

    def test_client(self):
        return self.app.test_client()

    def render_view(self, request, path=''):
        """Default controller.
        Render the template at `path` guessing it's mimetype.
        """
        path = self._normalize_path(path)
        fn, ext = splitext(path)
        real_ext = self._translate_ext(ext)
        fullpath = join(self.source_dir, path.lstrip('/'))

        if not exists(fullpath):
            ext, path, fullpath = self._get_alternative(path)
            if not fullpath:
                return self.not_found()

        plain_text_exts = self.settings.plain_text
        if ext in plain_text_exts or u.is_binary(fullpath):
            return send_file(request, fullpath)

        try:
            path = path.replace(sep, '/')
            resp = self.render(path, self.settings)
        except TemplateSyntaxError, e:
            print '-- WARNING:', 'Syntax error while trying to process', \
                    u.to_unicode(path), 'as a Jinja template.'
            source = u.get_source(fullpath)
            resp = Response(source)
            print e

        if real_ext == u'.html':
            resp.data = self._post_process(resp.data)

        resp.mimetype = mimetypes.guess_type('a' + real_ext)[0] or 'text/plain'
        return resp

    def build_views(self):
        processed = []
        views = []
        views_ignore = self.settings.views_ignore
        theme_prefix = self.settings.theme_prefix

        def callback(relpath_in):
            if relpath_in.endswith(views_ignore):
                return
            fn, ext = splitext(relpath_in)
            real_ext = self._translate_ext(ext)
            relpath_in_real = u'%s%s' % (fn, real_ext)
            relpath_out = relpath_in_real
            if theme_prefix and not relpath_out.startswith(theme_prefix):
                relpath_out = join(theme_prefix, relpath_out)
            print relpath_out

            path_in = join(self.source_dir, relpath_in)
            path_out = u.make_dirs(self.build_dir, relpath_out)

            plain_text_exts = self.settings.plain_text
            if ext in plain_text_exts or u.is_binary(path_in):
                return u.copy_if_has_change(path_in, path_out)

            try:
                relpath_in = relpath_in.replace(sep, '/')
                content = self.render(relpath_in, self.settings, to_string=True)
            except TemplateSyntaxError:
                print '-- WARNING:', 'Syntax error while trying to process', \
                    u.to_unicode(relpath_in), 'as a Jinja template.'
                content = u.get_source(path_in)
            
            if real_ext != ext:
                processed.append([relpath_in, relpath_in_real])

            if real_ext == u'.html':
                content = self._post_process(content)
                return views.append([relpath_in, path_out, content])

            u.make_file(path_out, content)
            return
        
        u.walk_dir(self.source_dir, callback, c.IGNORE)
        rx_processed = u.get_processed_regex(processed)
        views_list = []
        
        for relpath_in, path_out, content in views:
            content = u.absolute_to_relative(content, relpath_in,
                theme_prefix)
            content = u.replace_processed_names(content, rx_processed)
            u.make_file(path_out, content)
            views_list.append(u.to_unicode(relpath_in))
        
        self.build_views_list(views_list)

    def build_views_list(self, views):
        ignore = self.settings.views_list_ignore
        ignore.append(c.VIEWS_INDEX)
        real_views = []

        for v in views:
            if v in ignore or v.startswith(c.IGNORE):
                continue
            mdate = u.get_file_mdate(join(self.source_dir, v))
            fn, ext = splitext(v)
            real_ext = self._translate_ext(ext)
            v = '%s%s' % (fn, real_ext)
            real_views.append((v, u' / '.join(v.split('/')), mdate))
        
        content = self.render(
            c.VIEWS_INDEX, 
            {'views': real_views},
            to_string=True
        )
        relpath = join(self.settings.theme_prefix, c.VIEWS_INDEX)
        print relpath
        final_path = u.make_dirs(self.build_dir, relpath)
        u.make_file(final_path, content)

    def not_found(self):
        resp = self.render('notfound.html')
        resp.status_code = 404
        resp.mimetype = 'text/html'
        return resp

