# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
import inspect
import sys

from django.conf import settings

from ..shortcuts import render_to_json_response, json_response
from ...utils import json

def autodiscover():
    # Register api actions from all installed apps
    from django.utils.importlib import import_module
    from django.utils.module_loading import module_has_submodule
    for app in settings.INSTALLED_APPS:
        if app != 'api':
            mod = import_module(app)
            try:
                import_module('%s.views'%app)
            except:
                if module_has_submodule(mod, 'views'):
                    raise

def trim(docstring):
    if not docstring:
        return ''
    # Convert tabs to spaces (following the normal Python rules)
    # and split into a list of lines:
    lines = docstring.expandtabs().splitlines()
    # Determine minimum indentation (first line doesn't count):
    indent = sys.maxint
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Remove indentation (first line is special):
    trimmed = [lines[0].strip()]
    if indent < sys.maxint:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Strip off trailing and leading blank lines:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Return a single string:
    return '\n'.join(trimmed)


class ApiActions(dict):
    properties = {}
    versions = {}
    def __init__(self):

        def api(request, data):
            '''
            Returns a list of all api actions
            takes {
                code: boolean, // if true, return source code (optional)
                docs: boolean // if true, return doc strings (optional)
            }
            returns {
                actions: {
                    name: {
                        cache: boolean, // if false, don't cache results
                        code: string, // source code
                        doc: string // doc strings
                    },
                    ... // more actions
                }
            }
            '''
            docs = data.get('docs', False)
            code = data.get('code', False)
            version = getattr(request, 'version', None)
            if version:
                _actions = self.versions.get(version, {}).keys()
                _actions = list(set(_actions + self.keys()))
            else:
                _actions = self.keys()
            _actions.sort()
            actions = {}
            for a in _actions:
                actions[a] = self.properties[a]
                if docs:
                    actions[a]['doc'] = self.doc(a, version)
                if code:
                    actions[a]['code'] = self.code(a, version)
            response = json_response({'actions': actions})
            return render_to_json_response(response)
        self.register(api)

    def doc(self, name, version=None):
        if version:
            f = self.versions[version].get(name, self.get(name))
        else:
            f = self[name]
        return trim(f.__doc__)

    def code(self, name, version=None):
        if version:
            f = self.versions[version].get(name, self.get(name))
        else:
            f = self[name]
        if name != 'api' and hasattr(f, 'func_closure') and f.func_closure:
            fc = filter(lambda c: hasattr(c.cell_contents, '__call__'), f.func_closure)
            f = fc[len(fc)-1].cell_contents 
        info = f.func_code.co_filename[len(settings.PROJECT_ROOT)+1:]
        info = u'%s:%s' % (info, f.func_code.co_firstlineno)
        return info, trim(inspect.getsource(f))

    def register(self, method, action=None, cache=True, version=None):
        if not action:
            action = method.func_name
        if version:
            if not version in self.versions:
                self.versions[version] = {}
            self.versions[version][action] = method
        else:
            self[action] = method
        self.properties[action] = {'cache': cache}

    def unregister(self, action):
        if action in self:
            del self[action]

actions = ApiActions()

def error(request, data):
    '''
    This action is used to test API error codes. It should return a 503 error.
    '''
    success = error_is_success
    return render_to_json_response({})
actions.register(error)