# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division
import os.path
from os.path import join
import re
from datetime import datetime
from urllib2 import unquote
import mimetypes

from django import forms
from django.core.paginator import Paginator
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.db.models import Q, Avg, Count, Sum
from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404, redirect
from django.template import RequestContext
from django.conf import settings
from django.contrib.comments.forms import CommentForm
from django.contrib.comments.models import Comment
from django.contrib.comments.signals import comment_will_be_posted, comment_was_posted
from django.contrib.contenttypes.models import ContentType
from ox.utils import json
from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from ox.django.http import HttpFileResponse
import ox
from actions import actions
from user.models import get_user_json
from django.views.decorators.csrf import csrf_exempt 
from boxes.views import _getPageData

@csrf_exempt
def api(request):
    if request.META['REQUEST_METHOD'] == "OPTIONS":
        response = render_to_json_response({'status': {'code': 200,
                                                       'text': 'use POST'}})
        response['Access-Control-Allow-Origin'] = '*'
        return response
    if not 'action' in request.POST:
        methods = actions.keys()
        api = []
        for f in sorted(methods):
            api.append({'name': f,
                        'doc': actions.doc(f).replace('\n', '<br>\n')})
        context = RequestContext(request, {'api': api,
                                           'sitename': settings.SITENAME})
        return render_to_response('api.html', context)
    function = request.POST['action']
    #FIXME: possible to do this in f
    #data = json.loads(request.POST['data'])

    f = actions.get(function, None)
    if f:
        response = f(request)
    else:
        response = render_to_json_response(json_response(status=400,
                                text='Unknown function %s' % function))
    response['Access-Control-Allow-Origin'] = '*'
    return response

#def api_api(request):
#    '''
#        returns list of all known api action
#        return {'status': {'code': int, 'text': string},
#                'data': {actions: ['api', 'hello', ...]}}
#    '''
#    actions = globals().keys()
#    actions = map(lambda a: a[4:], filter(lambda a: a.startswith('api_'), actions))
#    actions.sort()
#    ret = {}
#    #FIXME: set cache to False for login, logout, etc.
#    for a in actions:
#        ret[a] = {
#            'cache': True
#        }
#    return render_to_json_response(json_response({'actions': ret}))

#def api_apidoc(request):
#    '''
#        returns array of actions with documentation
#    '''
#    actions = globals().keys()
#    actions = map(lambda a: a[4:], filter(lambda a: a.startswith('api_'), actions))
#    actions.sort()
#    docs = {}
#    for f in actions:
#        docs[f] = get_api_doc(f)
#    return render_to_json_response(json_response({'actions': docs}))


def init(request):
    '''
        return {'status': {'code': int, 'text': string},
                'data': {user: object}}
    '''
    #data = json.loads(request.POST['data'])
    response = json_response({})
    if request.user.is_authenticated():
        response['data']['user'] = get_user_json(request.user)
    else:
        response['data']['user'] = {'name': 'Guest', 'group': 'guest', 'level': 'guest', 'preferences': {}}
#FIXME: Get config definition out of here.
    response['data']['config'] = {
        'site': {
            'name': 'India Theatre Forum'
        }
    }
#    response['data']['page'] = _getPageData(request)
    return render_to_json_response(response)
actions.register(init)

def error(request):
    '''
        trows 503 error
    '''
    success = error_is_success
    return render_to_json_response({})
actions.register(error)

def find(request):
    data = json.loads(request.POST['data'])
#    print json.dumps(data)   
    model = getModel(data)
    response = json_response({})
    l = model.get_list(data)
    if data.has_key('ids'):
        ids = data['ids']
    else:
        ids = []
    if data.has_key('range'):
        response['data']['items'] = l
    else:
        response['data']['items'] = len(l)
        response['data']['positions'] = _get_positions(ids, l)
    response['status'] = {'code': 200}
    return render_to_json_response(response)
actions.register(find)

def _get_positions(ids, l):
    ret = {}
    i = 0
    for id in ids:
        for obj in l:
            if obj['id'] == id:
                ret[id] = i
                break
        i += 1
    return ret   


def preview(request):
    data = json.loads(request.POST['data'])
    if not data.has_key('id'):
        return render_to_json_response({'status': {'code': 404}})
    id = int(data['id'])
    model = getModel(data)
    response = json_response({})
    response['status'] = {'code': 200}
    obj = get_object_or_404(model, pk=id)
    response['data'] = obj.preview_dict()  
    response['template'] = getTemplate(data, "preview")
    return render_to_json_response(response)
actions.register(preview)

#FIXME: Generalize based on these two functions being the same.
def info(request):
    '''
        id: object id
        model: string, name of model
        module: string, name of module
    '''
    data = json.loads(request.POST['data'])
    id = int(data['id'])
    model = getModel(data)
    response = json_response({})
    response['status'] = {'code': 200}
    obj = get_object_or_404(model, pk=id)
    if model.hasComments:
        response['commentForm'] = _get_comment_form_initial(request, obj) 
        response['comments'] = _get_comments(obj)
    response['data'] = obj.info_dict()  
    response['template'] = getTemplate(data, "info")
    return render_to_json_response(response)    
actions.register(info)

#obj: Django model instance - object for which to get comment form for.
#Returns dict with content_type, object_id, security_hash, etc.
#FIXME: put comments stuff somewhere
def _get_comment_form_initial(request, obj):
    c = CommentForm(obj)
    return c.initial    

def _get_comments(obj):
    ret = []
    content_type = ContentType.objects.get_for_model(obj)
    object_id = obj.id
    qset = Comment.objects.filter(content_type=content_type, object_pk=object_id)
    for q in qset:
        ret.append({
            'name': q.name,
#            'date': q.submit_date,
            'comment': q.comment
        })
    return ret

@login_required_json
def addComment(request):
    '''
       id
       module
       model
       comment
       content_type
       email
       name
       object_pk
       security_hash
       timestamp 
    '''
    data = json.loads(request.POST['data'])
    data.update({
        'name': request.user.username,
        'email': request.user.email
    })
    id = int(data['object_pk'])
    model = getModel(data)    
    obj = get_object_or_404_json(model, pk=id)
    cf = CommentForm(obj, data=data)
    ret = json_response({})
    if cf.is_valid():
        comment = cf.get_comment_object()
        comment.user = request.user
            # Signal that the comment is about to be saved
        responses = comment_will_be_posted.send(
            sender  = comment.__class__,
            comment = comment,
            request = request
        )

        for (receiver, response) in responses:
            if response == False:
                return CommentPostBadRequest(
                    "comment_will_be_posted receiver %r killed the comment" % receiver.__name__)

        # Save the comment and signal that it was saved
        comment.save()
        comment_was_posted.send(
            sender  = comment.__class__,
            comment = comment,
            request = request
        )
        ret['data'] = {
            id: comment.id
        }
        ret['status'] = {'code': 200}
        
    else:
        ret['status'] = {'code': 200}
        ret['errors'] = cf.errors    
    return render_to_json_response(ret)
actions.register(addComment)

widgetMap = {
    'TextInput': 'text',
    'Textarea': 'textarea',
    'Select': 'select'
}

def getAddItemForm(request):    
    response = json_response({})
    data = json.loads(request.POST['data'])
    model = getModel(data)
#    import pdb; pdb.set_trace()
    form = getForm(data)
    form_fields = []
    for field in form.base_fields:
        name = field
        this = form.base_fields[field]
        widget = this.widget
        widgetClassName = type(widget).__name__

        if hasattr(widget, "choices"):
            choices = []
            for c in widget.choices:
                if c[0] ==  '':
                    id = 0
                else:
                    id = c[0]    
                choices.append({
                    'id': id,
                    'title': c[1]
                })
        else:
            choices = False

        js_widget = widgetMap.get(widgetClassName, "text")
        form_fields.append({
            'name': name,
            'widget': js_widget,
            'label': this.label,
            'choices': choices
        })
    response['data']['form'] = form_fields
    return render_to_json_response(response)  
actions.register(getAddItemForm)


@login_required_json
def addItem(request):
    response = json_response({})
    data = json.loads(request.POST['data'])
    model = getModel(data)
    form = getForm(data)
    if form.is_valid():
        m = model()
        for field in form.base_fields:
            m.__setattr__(field, form.cleaned_data[field])
        m.save()
        response['data'] = {'id': m.id}
    else:
        response['errors'] = form.errors
    return render_to_json_response(response)
actions.register(addItem)


def getTemplate(data, tmpl_name):
    path = join(settings.PROJECT_PATH, "templates", data['module'], data['model'], tmpl_name + ".html")
    return open(path).read().strip()

def getModel(data):
    module = __import__(data['module'])
    return module.models.__getattribute__(data['model'])

#FIXME: BAAD Hack
def getForm(data):
    module = __import__(data['module'] + ".forms")
    model = getModel(data)
    formStr = model.add_form    
    return module.forms.__getattribute__(formStr)


def get_api_doc(f):
    f = 'api_' + f

    import sys
    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)

    return trim(globals()[f].__doc__)

def apidoc(request):
    '''
        this is used for online documentation at http://127.0.0.1:8000/api/
    '''

    functions = filter(lambda x: x.startswith('api_'), globals().keys())
    api = []
    for f in sorted(functions):
        api.append({
            'name': f[4:],
            'doc': get_api_doc(f[4:]).replace('\n', '<br>\n')
        })
    context = RequestContext(request, {'api': api,
                                       'sitename': settings.SITENAME,})
    return render_to_response('api.html', context)

def jsdoc(request):
    '''
        Used to document OxUI JS Widgets
    '''
    context = RequestContext(request, {})
    return render_to_response("jsdoc.html", context)


'''
    ajax html snapshots
    http://code.google.com/web/ajaxcrawling/docs/html-snapshot.html    
'''
def html_snapshot(request):
    fragment = unquote(request.GET['_escaped_fragment_'])
    url = request.build_absolute_uri('/ra')
    url = 'http://'+settings.URL
    response = HttpResponse('sorry, server side rendering for %s!#%s not yet implemented'%(url, fragment))
    return response
