####################################
####################################
####################################
####
####  Notice
####
#
# This module is mostly deprecated,
# and is being gradually phased out,
# in favor of the new REST API,
# which is in flashcards.views.rest
#
####################################
 
 
 
# Some views which should be considered part of the REST API are contained 
# in the reviews.py module. This module contains the rest of them.
 
import random
 
from cachecow.decorators import cached_view
from django.contrib.auth.decorators import login_required
from django.forms import forms
from django.forms.models import modelformset_factory
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.template import RequestContext, loader
from django.utils import simplejson
from django.views.decorators.cache import cache_page
from django.views.decorators.http import require_GET
from django.views.generic.create_update import (update_object, delete_object,
                                                create_object)
from dojango.decorators import json_response
from dojango.util import to_dojo_data, json_decode, json_encode
 
from apps.utils import japanese
from apps.utils.querycleaner import clean_query
from flashcards.cachenamespaces import fact_grid_namespace
from flashcards.forms import DeckForm, FactForm, FieldContentForm, CardForm
from flashcards.models import (FactType, Fact, Deck, CardTemplate, FieldType,
                               FieldContent, Card)
from flashcards.models.constants import MAX_NEW_CARD_ORDINAL
from flashcards.views.decorators import (flashcard_api as api, api_data_response,
                                         ApiException, has_card_query_filters,
                                         flashcard_api_with_dojo_data as api_dojo_data)
from flashcards.views.shortcuts import get_deck_or_404
from flashcards.signals import fact_deleted
 
#import logging
#logger = logging.getLogger(__name__)
 
 
#FIXME add permissions validation for every method (The important ones have it)
 
 
 
@api
def rest_deck_subscribe(request, deck_id):
    if request.method == 'POST':
        deck = get_deck_or_404(request.user, deck_id)
        new_deck = deck.subscribe(request.user)
 
        return {'deckId': new_deck.id,
                'postRedirect': new_deck.get_absolute_url()}
 
@cache_page(60 * 60 * 24 * 7) # 7 days
@api
def rest_generate_reading(request):
    if request.method == 'POST':
        return japanese.generate_reading(request.POST['expression'])
 
@api
def rest_deck(request, deck_id):
    deck = get_deck_or_404(request.user, deck_id)
 
    if request.method == 'DELETE':
        if deck.subscriber_decks.filter(active=True).exists(): 
            deck.active = False
            deck.save()
        else:
            deck.delete_cascading()
    elif request.method == 'PUT':
        params = clean_query(request.POST, {'shared': bool})
        # change shared status
        if params.get('shared') is not None:
            if params['shared']:
                if deck.synchronized_with:
                    raise ApiException
                deck.share()
            else:
                if not deck.shared:
                    raise ApiException
                deck.unshare()
        return {'shared': deck.shared}
 
@api
def rest_deck_name(request, deck_id):
    deck = get_deck_or_404(request.user, deck_id)
 
    if request.method == 'GET':
        return deck.name
    elif request.method == 'POST':
        deck.name = request.POST['name']
        deck.save()
 
@api
def rest_deck_description(request, deck_id):
    deck = get_deck_or_404(request.user, deck_id)
 
    if request.method == 'GET':
        return deck.description
    elif request.method == 'POST':
        deck.description = request.POST['description']
        deck.save()
 
 
@api_dojo_data
@login_required
def rest_card_templates(request, fact_type_id):
    '''Returns list of CardTemplate objects given a parent FactType id'''
    fact_type = get_object_or_404(FactType, pk=fact_type_id)
    return to_dojo_data(fact_type.cardtemplate_set.all())
 
 
@api_dojo_data
@login_required
def rest_fields(request, fact_type_id):
    '''Returns list of Field objects given a FactType id'''
    fact_type = get_object_or_404(FactType, pk=fact_type_id)
    return to_dojo_data(fact_type.fieldtype_set.all().order_by('ordinal'))
 
@api_dojo_data
@login_required
def rest_fact_types(request):
    return to_dojo_data(FactType.objects.all())
 
 
 
@api_dojo_data
@login_required
def rest_cards(request): #todo:refactor into facts (no???)
    '''
    Returns the cards for a given fact. Accepts `fact` in the GET params.
    '''
    if request.GET['fact']:
        fact = get_object_or_404(Fact, pk=request.GET['fact'])
        return to_dojo_data(fact.card_set.get_query_set())
 
 
@api_dojo_data
@login_required
def rest_card_templates_for_fact(request, fact_id):
    '''
    Returns a list of card templates for which the given fact 
    has corresponding cards activated.
    '''
    fact = get_object_or_404(Fact, pk=fact_id)
    activated_card_templates = list(
        e.template for e in fact.card_set.filter(active=True))
 
    card_templates = []
    for card_template in fact.fact_type.cardtemplate_set.all():
        #TODO only send the id(uri)/name/status
        card_templates.append({
            'card_template': card_template,
            'activated_for_fact': 
                (card_template in activated_card_templates),
        })
    return to_dojo_data(card_templates, identifier=None)
 
 
@login_required
@require_GET
def rest_facts_tags(request):
    '''
    Returns a JSON list of tags. Nothing more.
    '''
    term = request.GET.get('term', None)
    tags = Fact.objects.all_tags_per_user(request.user)
    ret = [tag.name for tag in tags if (term and tag.name.startswith(term))]
    return HttpResponse(simplejson.dumps(ret), mimetype='application/json')
 
    #TODO we can use `term` which jquery's autocomplete gives us to search prefixes -- an optimization
    #for now we just give back all the tags
    #term = request.GET.get('term', None)
 
    #tags = [{'name': tag.name, 'id': tag.id} for tag in tags]
    #return to_dojo_data(tags)
 
#FIXME issue is w/ reloading the server but not memcached.
# the things below get lost (the conns)
# to fix - just use versioning on the keys? somehow increment every time
# the process is started.
 
 
@cached_view(namespace=lambda request, *args, **kwargs:
                           fact_grid_namespace(request.GET.get('deck')),
             timeout=(3600 * 24 * 6)) # 6 day timeout
@api_dojo_data
@has_card_query_filters
def rest_facts(request, deck=None, tags=None): 
    #TODO refactor into facts (no???)
    if request.method == 'GET':
        ret = []
        if request.GET['fact_type']:
            fact_type_id = request.GET['fact_type'] 
            fact_type = get_object_or_404(FactType, pk=fact_type_id)
 
            user = deck.owner if deck else request.user
 
            facts = Fact.objects.with_upstream(
                user, deck=deck, tags=tags).filter(active=True)
 
            #is the user searching his facts?
            if ('search' in request.GET
                    and request.GET['search'].strip()):
                search_query = request.GET['search']
                facts = Fact.objects.search(
                    fact_type, search_query, query_set=facts)
                #FIXME add search for synchronized facts too!
 
            for fact in facts.iterator():
                row = {
                    'fact-id': fact.id, 
                    'suspended': fact.suspended(),
                }
 
                ident, name = '', ''
                for field_content in fact.field_contents:
                    #TODO rename to be clearer, like field_id, or ???
                    key = 'id{0}'.format(field_content.field_type_id) 
 
                    if not ident:
                        ident = key
                    elif not name:
                        name = key
 
                    row[key] = field_content.human_readable_content()
                    row['{0}_field-content-id'.format(key)] = field_content.id
 
                if not name:
                    name = ident
 
                ret.append(row)
            ret = to_dojo_data(ret)
            ret['identifier'] = 'fact-id'
            #ret['name'] = name #todo:for <2 cols/fields...?
            return ret
    elif request.method == 'POST':
        # Create fact in deck, including its fields and cards. POST method.
        #TODO refactor into other module probably
        ret = {}
 
        #TODO just get this from the form object.
        deck = get_deck_or_404(request.user, request.POST['fact-deck'],
                               must_own=True)
 
        # Override the submitted deck ID with the ID from the URL, 
        # since this is a RESTful interface.
        post_data = request.POST.copy()
        #post_data['fact-deck'] = deck_id
 
        #todo: refactor this into model code
 
        #CardFormset = modelformset_factory(Card, exclude=('fact', 'ease_factor', )) #TODO make from CardForm
        #card_formset = CardFormset(post_data, prefix='card')
        card_templates = CardTemplate.objects.filter(
                id__in=[e[1] for e in post_data.items()
                if e[0].find('card_template') == 0])
 
        #FieldContentFormset = modelformset_factory(FieldContent, exclude=('fact', ))
        FieldContentFormset = modelformset_factory(
            FieldContent, form=FieldContentForm)
        field_content_formset = FieldContentFormset(
            post_data, prefix='field_content')
 
        fact_form = FactForm(post_data, prefix='fact')
 
        if field_content_formset.is_valid() and fact_form.is_valid():
            #TODO automate the tag saving in forms.py
            new_fact = fact_form.save() 
            new_fact.active = True
            new_fact.save()
 
            # maps subfact group numbers to the subfact object
            group_to_subfact = {} 
            for field_content_form in field_content_formset.forms:
                #TODO don't create fieldcontent objects for 
                # optional fields which were left blank.
                new_field_content = field_content_form.save(commit=False)
                # is this a field of the parent fact, or a subfact?
                if (new_field_content.field_type.fact_type
                    == new_fact.fact_type):
                    # parent fact
                    new_field_content.fact = new_fact
                else:
                    # subfact
                    group = field_content_form\
                            .cleaned_data['subfact_group']
                    if group not in group_to_subfact.keys():
                        # create the new subfact
                        new_subfact = Fact(
                                fact_type=new_field_content\
                                          .field_type.fact_type,
                                active=True,
                                #deck=new_fact.deck,
                                parent_fact=new_fact,
                        )
                        new_subfact.save()
                        group_to_subfact[group] = new_subfact
                    new_field_content.fact = group_to_subfact[group]
                new_field_content.save()
 
            for card_template in card_templates: 
                #card_form in card_formset.forms:
                new_card = Card(
                    template=card_template,
                    fact=new_fact,
                    active=True,
                    priority = 0)
                new_card.randomize_new_order()
                new_card.save()
        else:
            raise ApiException({
                #'card': card_formset.errors,
                'field_content': field_content_formset.errors,
                'fact': [fact_form.errors]
            })
        return ret
 
@api
def rest_fact_suspend(request, fact_id):
    if request.method == 'POST':
        Fact.objects.get_for_owner_or_subscriber(
            fact_id, request.user).suspend()
 
@api
def rest_fact_unsuspend(request, fact_id):
    if request.method == 'POST':
        Fact.objects.get_for_owner_or_subscriber(
            fact_id, request.user).unsuspend()
 
@api
def rest_fact(request, fact_id): #todo:refactor into facts
    if request.method == 'POST':
        # Update fact
 
        # Override the submitted deck ID with the ID from the URL.
        post_data = request.POST.copy()
 
        #todo: refactor this into model code
 
        # if this fact is a shared fact which the current subscribing user 
        # hasn't copied yet, copy it first
        fact = Fact.objects.get_for_owner_or_subscriber(fact_id, request.user)
 
        #fact_form = FactForm(post_data, prefix='fact', instance=fact)
        FactFormset = modelformset_factory(
                Fact, fields=('id', 'fact_type',), can_delete=True)
        fact_formset = FactFormset(
                post_data, prefix='fact',
                queryset=Fact.objects.filter(id=fact.id)|fact.subfacts)
 
        #TODO make from CardForm
        CardFormset = modelformset_factory(
                Card, exclude=('fact', 'ease_factor', )) 
        card_formset = CardFormset(
                post_data, prefix='card',
                queryset=fact.card_set.get_query_set())
 
        FieldContentFormset = modelformset_factory(
                FieldContent, form=FieldContentForm)
        field_content_queryset = (fact.fieldcontent_set.get_query_set() 
                                  or None)
        field_content_formset = FieldContentFormset(
                post_data, prefix='field_content') 
                #, queryset=field_content_queryset)
 
        #fact_form = FactForm(post_data, prefix='fact', instance=fact)
        # ^^^^^^^ this isn't updated
        if (card_formset.is_valid()
                and field_content_formset.is_valid()
                and fact_formset.is_valid()):
            #fact = fact_form.save() #TODO needed in future?
 
            #update the fact's assigned deck
            #FIXME catch error if does not exist
            #deck_id = int(post_data['fact-deck'])
            #fact.deck = Deck.objects.get(id=deck_id)
            #fact.save()
 
            # maps subfact group numbers to the subfact object
            group_to_subfact = {} 
            for field_content_form in field_content_formset.forms:
                field_content = field_content_form.save(commit=False)
 
                # is this a field of the parent fact, or a subfact?
                if field_content.field_type.fact_type == fact.fact_type:
                    # Parent fact.
                    field_content.fact = fact
                    field_content.save()
                else:
                    # Subfact.
                    # Does this subfact already belong to the user?
                    # If not, create it, only if anything's changed.
                    # Or, create it, if it's new.
                    if field_content_form.cleaned_data['id']:
                        # existing field content
 
                        # if it's part of a subfact that's being 
                        # deleted in this form, ignore the field.
                        if field_content_form.cleaned_data['id'].fact in\
                            [fact_form.cleaned_data['id'] for fact_form
                             in fact_formset.deleted_forms]:
                            continue
 
                        if (field_content_form.cleaned_data['id'].fact.owner
                                == request.user):
                            #TODO is this necessary? vvv
                            field_content.fact = \
                                    field_content_form.cleaned_data['id'].fact
                            field_content.save()
                        else:
                            original = field_content_form.cleaned_data['id']
                            if (field_content_form['content']
                                    != original.content):
                                # user updated subscribed subfact content 
                                # - so create his own subscriber subfact to
                                # hold it.
                                new_subfact = original.fact.copy_to_parent_fact(
                                        fact, copy_field_contents=True)
                                new_field_content = new_subfact.fieldcontent_set.get(
                                        field_type=field_content_form.cleaned_data['field_type'])
                                new_field_content.content = \
                                        field_content_form.cleaned_data['content']
                                new_field_content.save()
                            else:
                                # not user's own, but he didn't update it anyway
                                pass
                    else:
                        # new field content
                        # this means new subfact.
                        # otherwise, this doesn't make sense unless the subfact 
                        # model changed - which isn't supported yet.
                        # or subscriber fields are optimized to not copy over 
                        # until modified
                        group = field_content_form.cleaned_data['subfact_group']
                        if group not in group_to_subfact.keys():
                            # create the new subfact
                            new_subfact = Fact(
                                fact_type=field_content.field_type.fact_type,
                                active=True,
                                parent_fact=fact
                            )
                            new_subfact.save()
                            group_to_subfact[group] = new_subfact
                        field_content.fact = group_to_subfact[group]
                        field_content.save()
 
 
            # delete any subfacts as needed
            for subfact_form in fact_formset.deleted_forms:
                subfact = subfact_form.cleaned_data['id']
                # make sure it's a subfact
                if subfact.parent_fact:# == fact:
                    if subfact.synchronized_with or subfact.parent_fact != fact:
                        # this is a subscriber fact
                        if subfact.synchronized_with:
                            subfact.active = False
                            subfact.save()
                        else:
                            # the user doesn't have his own copy of this 
                            # subfact yet
                            new_subfact = subfact.copy_to_parent_fact(
                                    fact, copy_field_contents=False)
                            new_subfact.active = False
                            new_subfact.save()
                    else:
                        subfact.delete()
 
 
            # disable any existing cards that weren't selected in the update,
            # or enable if selected and create if needed
            # do all this for subscribers too, if this is in a shared deck
            facts = Fact.objects.filter(id=fact.id)
            if fact.subscriber_facts.all():
                facts = facts | fact.subscriber_facts.all()
            for fact2 in facts.iterator():
                card_form_template_ids = dict(
                        (card_form.cleaned_data['template'].id, card_form)
                        for card_form in card_formset.forms)
                for card_template in fact.fact_type.cardtemplate_set.all():
                    if card_template.id in card_form_template_ids.keys():
                        try:
                            card = fact2.card_set.get(template=card_template)
                            card.activate()
                        except Card.DoesNotExist:
                            #card_form = card_form_template_ids
                            #[card_template.id]
                            #new_card = card_form.save(commit=False)
                            new_card = Card(template=card_template)
                            new_card.fact = fact2
                            new_card.active = True
                            new_card.randomize_new_order()
                            new_card.save()
                    else:
                        #card was not selected in update, so disable it 
                        # if it exists
                        try:
                            card = fact2.card_set.get(template=card_template)
                            if not card.active:
                                continue
                            elif fact2.synchronized_with and card.review_count:
                                # don't disable subscriber cards which have 
                                # already been reviewed
                                continue
                            card.deactivate()
                        except Card.DoesNotExist:
                            pass
        else:
            raise ApiException({
                'card': card_formset.errors,
                'fact': fact_formset.errors,
                'field_content': field_content_formset.errors,
            })
    elif request.method == 'DELETE':
        fact = Fact.objects.get_for_owner_or_subscriber(fact_id, request.user)
        deleted_fact = fact.delete_for_user(request.user)
        fact_deleted.send(deleted_fact)