# -*- coding: utf-8 -*-

import operator

from django.contrib.auth.models import User
from django.db.models import Q

from accounts.models import get_anon_profile
from dictionary.common_func import escape_regex
from dictionary.forms import FilterForm
from dictionary.models import Argument, Frame, Frame_Char_Model, Frame_Opinion_Value, \
                              Lemma, Lemma_Status, NKJP_Example, NKJP_Source, POS, \
                              Position, Vocabulary, \
                              get_frame_char_and_its_value, get_schemata_by_type
from semantics.forms import GeneralSelPrefForm, RelationalSelPrefForm, RoleForm, \
                            SynsetSelPrefForm
from semantics.models import Complement, FrameOpinion, RelationalSelectivePreference, \
                             SemanticFrame
from wordnet.models import LexicalUnit

def schemata_filter_options():
    # pobieranie wartosci aspektu
    aspect_model = Frame_Char_Model.objects.get(model_name=u'ASPEKT')
    aspect_vals_objs = aspect_model.frame_char_values.order_by('-priority')
    aspect_options = [{'name': '*', 'value': '*'}]
    aspect_options.extend([{'name': val.value, 'value': val.value} for val in aspect_vals_objs])
    
    # pobieranie wartosci zwrotnosci
    reflex_model = Frame_Char_Model.objects.get(model_name=u'ZWROTNOŚĆ')
    reflex_vals_objs = reflex_model.frame_char_values.order_by('-priority')
    reflex_options = [{'name': '*', 'value': '*'}]
    reflex_options.extend([{'name': val.value, 'value': val.value} for val in reflex_vals_objs])
      
    # pobieranie wartosci negatywnosci
    neg_model = Frame_Char_Model.objects.get(model_name=u'NEGATYWNOŚĆ')
    neg_vals_objs = neg_model.frame_char_values.order_by('-priority')
    neg_options = [{'name': '*', 'value': '*'}]
    neg_options.extend([{'name': val.value, 'value': val.value} for val in neg_vals_objs])
      
    # pobieranie wartosci predykatywnosci
    pred_model = Frame_Char_Model.objects.get(model_name=u'PREDYKATYWNOŚĆ')
    pred_vals_objs = pred_model.frame_char_values.order_by('-priority')
    pred_options = [{'name': '*', 'value': '*'}]
    pred_options.extend([{'name': val.value, 'value': val.value} for val in pred_vals_objs])
      
    # pobieranie opinii o schemacie
    opinion_options = [{'name': '*', 'value': '*'}]
    opinion_options.extend([{'name': val.value, 'value': val.value} for val in Frame_Opinion_Value.objects.order_by('priority')])
    
    schema_type_options = [{'name': '*', 'value': '*'},
                           {'name': 'normalny', 'value': 'normal'}, 
                           {'name': 'frazeologiczny', 'value': 'phraseologic'}]
  
    return {'schema_type_options': schema_type_options,
            'reflex_options': reflex_options,
            'aspect_options': aspect_options,
            'neg_options': neg_options,
            'pred_options': pred_options,
            'opinion_options': opinion_options}

def all_filter_rules_loaded(rules):
    if set(default_filter_rules().keys()) != set(rules):
        return False
    return True

def default_filter_rules():
    return {'lemma': '.*',
            'pos': None,
            'contains_phraseology': None,
            'owner': None,
            'phraseologist': None,
            'semanticist': None,
            'vocabulary': None,
            'status': None,
            'example_source': None,
            'approver': None,
            'reflex': None,
            'negativity': None,
            'predicativity': None,
            'aspect': None,
            'argument': '.*',
            'position': '.*',
            'schema_opinion' : None,
            'sender': None,
            'schema_type': None,
            'frame_opinion': None,
            'sem_arguments': []}

def prepare_filter_form(request):
    if request.session.has_key('lemma_preview') and request.session['lemma_preview']:
        if not request.session.has_key('filter_rules_lemma_preview'):
            request.session['filter_rules_lemma_preview'] = default_filter_rules()
        filter_rules = request.session['filter_rules_lemma_preview']
    else:
        if not request.session.has_key('filter_rules'):
            request.session['filter_rules'] = default_filter_rules()
        filter_rules = request.session['filter_rules']
      
    users = User.objects.none()
    phraseologists = User.objects.none()
    semanticists = User.objects.none()
    vocabularies = Vocabulary.objects.none()
    senders = User.objects.none()
    statuses = get_anon_profile().visible_statuses.all()
    can_confirm_example = False
    if request.user.is_authenticated():
        users = User.objects.filter(lemmas__old=False).distinct().order_by('username')
        phraseologists = User.objects.filter(phraseologist_lemmas__old=False).distinct().order_by('username')
        semanticists = User.objects.filter(semanticist_lemmas__old=False).distinct().order_by('username')
        vocabularies = request.user.visible_vocabularies.all()
        senders = User.objects.order_by('groups__group_settings__priority')
        statuses = Lemma_Status.objects.all()
        if request.user.has_perm('dictionary.confirm_example') or request.user.is_superuser:
            can_confirm_example = True
      
    form = FilterForm(users=users, 
                      phraseologists=phraseologists,
                      semanticists=semanticists,
                      vocabularies=vocabularies, 
                      senders=senders,
                      statuses=statuses,
                      lemma=filter_rules['lemma'],
                      sel_pos=filter_rules['pos'],
                      contains_phraseology=filter_rules['contains_phraseology'],
                      sel_user=filter_rules['owner'], 
                      sel_phraseologist=filter_rules['phraseologist'],
                      sel_semanticist=filter_rules['semanticist'],
                      sel_vocabulary=filter_rules['vocabulary'], 
                      sel_status=filter_rules['status'],
                      sel_reflex=filter_rules['reflex'], 
                      sel_negativity=filter_rules['negativity'],
                      sel_predicativity=filter_rules['predicativity'],
                      sel_aspect=filter_rules['aspect'],
                      sel_has_argument=filter_rules['argument'], 
                      sel_has_position=filter_rules['position'],
                      sel_schema_opinion=filter_rules['schema_opinion'],
                      can_confirm_example = can_confirm_example,
                      sel_example_source=filter_rules['example_source'],
                      sel_approver=filter_rules['approver'],
                      sel_sender=filter_rules['sender'],
                      sel_schema_type=filter_rules['schema_type'],
                      sel_frame_opinion=filter_rules['frame_opinion'])
    return {'form': form,
            'sem_args_forms': sem_args_to_forms(filter_rules['sem_arguments'])}

def sem_args_to_forms(sem_arguments):
    args_forms = []
    first_alternative = True
    for alternative in sem_arguments:
        if first_alternative:
            first_alternative = False
        else:
            args_forms.append('or')
        for arg in alternative:
            args_forms.append(RoleForm(negation=arg['negation'], 
                                       sel_role=arg['role'], 
                                       sel_attribute=arg['attribute'],
                                       sel_preferences=get_sel_prefs_as_forms(arg)))
    return args_forms

def get_sel_prefs_as_forms(arg):
    forms = []
    if arg['general_prefs']:
        forms.extend(general_prefs_to_forms(arg['general_prefs']))
    if arg['synset_prefs']:
        forms.extend(synset_prefs_to_forms(arg['synset_prefs']))
    if arg['relational_prefs']:
        forms.extend(relational_prefs_to_forms(arg['relational_prefs']))
    return forms
    
def general_prefs_to_forms(prefs):
    forms = []
    for pref in prefs:
        forms.append(GeneralSelPrefForm(sel_preference=pref))
    return forms

def synset_prefs_to_forms(prefs):
    forms = []
    for pref in prefs:
        forms.append(SynsetSelPrefForm(sel_preference=pref))
    return forms

def relational_prefs_to_forms(prefs):
    forms = []
    for pref in prefs:
        forms.append(RelationalSelPrefForm(sel_relation=pref['relation'], 
                                           sel_role=pref['role'], 
                                           sel_attribute=pref['attribute']))
    return forms

def save_lemma_filters_and_get_schemata_filter_setup(request, filter_dict):
  if filter_dict['pos']:
    pos_obj = POS.objects.get(id=filter_dict['pos'])
  else:
    pos_obj = None

  if filter_dict['owner']:
    owner_obj = User.objects.get(id=filter_dict['owner'])
  else:
    owner_obj = None
    
  if filter_dict['phraseologist']:
    phraseologist_obj = User.objects.get(id=filter_dict['phraseologist'])
  else:
    phraseologist_obj = None
    
  if filter_dict['semanticist']:
    semanticist_obj = User.objects.get(id=filter_dict['semanticist'])
  else:
    semanticist_obj = None
    
  if filter_dict['vocabulary']:
    vocabulary_obj = Vocabulary.objects.get(name=filter_dict['vocabulary'])
  else:
    vocabulary_obj = None
    
  if filter_dict['status']:
    status_obj = Lemma_Status.objects.get(id=filter_dict['status'])
  else:
    status_obj = None
    
  if filter_dict['example_source']:
    nkjp_source_obj = NKJP_Source.objects.get(id=filter_dict['example_source'])
  else:
    nkjp_source_obj = None
    
  if filter_dict['approver']:
    approver_obj = User.objects.get(id=filter_dict['approver'])
  else:
    approver_obj = None
    
  if filter_dict['has_message_from']:
    try:
      sender_obj = User.objects.get(pk=filter_dict['has_message_from'])
    except User.DoesNotExist:
      sender_obj = None
  else:
    sender_obj = None
    
  reflex_obj, reflex_val = get_frame_char_and_its_value(filter_dict['reflex'], '*')
  negativity_obj, negativity_val = get_frame_char_and_its_value(filter_dict['negativity'], '*')
  aspect_obj, aspect_val = get_frame_char_and_its_value(filter_dict['aspect'], '*')
  pred_obj, pred_val = get_frame_char_and_its_value(filter_dict['predicativity'], '*')
  
  if filter_dict['schema_opinion']:
    schema_opinion_obj = Frame_Opinion_Value.objects.get(id=filter_dict['schema_opinion'])
    opinion_val = schema_opinion_obj.value
  else:
    schema_opinion_obj = None
    opinion_val = '*'
  
  if 'schema_type' in filter_dict:
      schema_type = filter_dict['schema_type']
  else:
      schema_type = None
      
  if 'frame_opinion' in filter_dict and filter_dict['frame_opinion']:
      frame_opinion = FrameOpinion.objects.get(id=filter_dict['frame_opinion'])
  else:
      frame_opinion = None
  
  sem_arguments = [constraints for constraints in filter_dict['sem_arguments'] if constraints != []]
      
  if request.session.has_key('lemma_preview') and request.session['lemma_preview']:
    request.session['filter_rules_lemma_preview'] = {'pos'                 : pos_obj,
                                                     'contains_phraseology': filter_dict['contains_phraseology'],
                                                     'owner'               : owner_obj,
                                                     'phraseologist'       : phraseologist_obj,
                                                     'semanticist'         : semanticist_obj,
                                                     'vocabulary'          : vocabulary_obj,
                                                     'status'              : status_obj,
                                                     'example_source'      : nkjp_source_obj,
                                                     'approver'            : approver_obj,
                                                     'reflex'              : reflex_obj,
                                                     'negativity'          : negativity_obj,
                                                     'predicativity'       : pred_obj,
                                                     'aspect'              : aspect_obj,
                                                     'argument'            : filter_dict['has_argument'],
                                                     'position'            : filter_dict['has_position'],
                                                     'lemma'               : filter_dict['lemma'],
                                                     'schema_opinion'      : schema_opinion_obj,
                                                     'sender'              : sender_obj,
                                                     'schema_type'         : schema_type,
                                                     'frame_opinion'       : frame_opinion,
                                                     'sem_arguments'       : sem_arguments}    
  else:
    request.session['filter_rules'] = {'pos'                 : pos_obj,
                                       'contains_phraseology': filter_dict['contains_phraseology'],
                                       'owner'               : owner_obj,
                                       'phraseologist'       : phraseologist_obj,
                                       'semanticist'         : semanticist_obj,
                                       'vocabulary'          : vocabulary_obj,
                                       'status'              : status_obj,
                                       'example_source'      : nkjp_source_obj,
                                       'approver'            : approver_obj,
                                       'reflex'              : reflex_obj,
                                       'negativity'          : negativity_obj,
                                       'predicativity'       : pred_obj,
                                       'aspect'              : aspect_obj,
                                       'argument'            : filter_dict['has_argument'],
                                       'position'            : filter_dict['has_position'],
                                       'lemma'               : filter_dict['lemma'],
                                       'schema_opinion'      : schema_opinion_obj,
                                       'sender'              : sender_obj,
                                       'schema_type'         : schema_type,
                                       'frame_opinion'       : frame_opinion,
                                       'sem_arguments'       : sem_arguments}
      
  return {'filter_frames': filter_dict['filter_frames'],
          'schema_type'  : schema_type,
          'reflex'       : reflex_val,
          'negativity'   : negativity_val,
          'predicativity': pred_val,
          'opinion'      : opinion_val,
          'aspect'       : aspect_val,
          'position'     : filter_dict['has_position'],
          'argument'     : filter_dict['has_argument']}

def filter_lemmas(lemmas, filter_rules, user):
    lemmas = filter_by_lemma_properties(lemmas, filter_rules, user)
    lemmas = filter_by_schemata(lemmas, filter_rules)
    lemmas = filter_by_frames(lemmas, filter_rules)  
    return lemmas
  
def filter_by_lemma_properties(lemmas, filter_rules, user):
    if filter_rules['owner']:
        lemmas = lemmas.filter(owner=filter_rules['owner'])
    if filter_rules['phraseologist']:
        lemmas = lemmas.filter(phraseologist=filter_rules['phraseologist'])
    if filter_rules['semanticist']:
        lemmas = lemmas.filter(semanticist=filter_rules['semanticist'])
    if filter_rules['vocabulary']:
        lemmas = lemmas.filter(vocabulary=filter_rules['vocabulary'])
    if filter_rules['status']:
        lemmas = lemmas.filter(status=filter_rules['status']) 
    if filter_rules['schema_opinion']:
        lemmas = lemmas.filter(frame_opinions__value=filter_rules['schema_opinion'])
    if filter_rules['lemma'] and filter_rules['lemma'] != '.*':
        lemmas = lemma_regex_filter(lemmas, filter_rules['lemma'])
    if filter_rules['sender']:
        lemmas = lemmas.filter(messages__sender=filter_rules['sender'])
    if filter_rules['pos']:
        lemmas = lemmas.filter(entry_obj__pos=filter_rules['pos'])
    if filter_rules['contains_phraseology']:
        phraseologic_lemmas = lemmas.filter(frames__phraseologic=True)
        if filter_rules['contains_phraseology'] == 'yes':
            lemmas = phraseologic_lemmas
        else:
            lemmas = lemmas.exclude(pk__in=phraseologic_lemmas)
    if filter_rules['example_source']:
        lemmas = lemmas.filter(Q(nkjp_examples__source=filter_rules['example_source']) &
                               Q(nkjp_examples__approved=False)).distinct()
        napproved_examples = NKJP_Example.objects.filter(Q(source=filter_rules['example_source']) & 
                                                         Q(approved=False) &
                                                         Q(lemmas__old=False) &
                                                         ~Q(approvers=user)).distinct()
    
        if filter_rules['approver']:
            napproved_examples = napproved_examples.filter(approvers=filter_rules['approver'])
        lemmas = lemmas.filter(nkjp_examples__in=napproved_examples)
    lemmas = lemmas.distinct()
    return lemmas

def lemma_regex_filter(lemmas, string):
    try:
        alternative_queries = []
        for alternative in string.split('|'):
            possible_lemmas = lemmas
            for conj in alternative.split('&'):
                model_results = []
                negation = False
                conj = conj.strip()
                if conj.startswith('!'):
                    conj = conj.lstrip('!')
                    negation = True
                regex = ur'^%s$' % escape_regex(conj)
                model_results = Lemma.objects.filter(old=False,
                                                     entry_obj__name__regex=regex).distinct()
                if model_results.exists():
                    if negation:
                        possible_lemmas = possible_lemmas.exclude(pk__in=model_results)
                    else:
                        possible_lemmas = possible_lemmas.filter(pk__in=model_results)
                elif not model_results.exists() and not negation:
                    possible_lemmas = Lemma.objects.none()
            alternative_queries.append(Q(id__in=possible_lemmas))
        lemmas = lemmas.filter(reduce(operator.or_, alternative_queries)).distinct()
    except:
        lemmas = Lemma.objects.none()
    return lemmas

def filter_by_schemata(lemmas, filter_rules):
    schemata = Frame.objects
    if filter_rules['reflex']:
        schemata = schemata.filter(characteristics=filter_rules['reflex'])
    if filter_rules['negativity']:
        schemata = schemata.filter(characteristics=filter_rules['negativity'])
    if filter_rules['predicativity']:
        schemata = schemata.filter(characteristics=filter_rules['predicativity'])
    if filter_rules['aspect']:
        schemata = schemata.filter(characteristics=filter_rules['aspect'])
    if filter_rules['position'] and filter_rules['position'] != '.*':
        schemata = pos_regex_frames(schemata, filter_rules['position'])
    if filter_rules['argument'] and filter_rules['argument'] != '.*':
        schemata = arg_regex_frames(schemata, filter_rules['argument'])
    if filter_rules['schema_type']:
        schemata = get_schemata_by_type(filter_rules['schema_type'], schemata)
       
    if (filter_rules['reflex'] or filter_rules['negativity'] or 
        filter_rules['aspect'] or filter_rules['predicativity'] or
        filter_rules['schema_type'] or filter_rules['schema_opinion'] or
        (filter_rules['argument'] and filter_rules['argument'] != '.*') or
        (filter_rules['position'] and filter_rules['position'] != '.*')):
        if filter_rules['schema_opinion']:
            lemmas = lemmas.filter(frame_opinions__frame__in=schemata.all(),
                                   frame_opinions__value=filter_rules['schema_opinion'])
        else:
            lemmas = lemmas.filter(frames__in=schemata.all()) 
    lemmas = lemmas.distinct()
    return lemmas

def pos_regex_frames(frames, string):
    try:
        alternative_queries = []
        for alternative in string.split('|'):
            possible_frames = frames
            for conj in alternative.split('&'):
                model_results = []
                negation = False
                conj = conj.strip()
                if conj.startswith('!'):
                    conj = conj.lstrip('!')
                    negation = True
                regex = ur'^%s$' % escape_regex(conj)
                model_results = Position.objects.filter(frames__lemmas__old=False,
                                                        text_rep__regex=regex).distinct()
                if model_results.exists():
                    if negation:
                        possible_frames = possible_frames.exclude(positions__in=model_results)
                    else:
                        possible_frames = possible_frames.filter(positions__in=model_results)
                elif not model_results.exists() and not negation:
                    possible_frames = Frame.objects.none()
            alternative_queries.append(Q(id__in=possible_frames))
        frames = frames.filter(reduce(operator.or_, alternative_queries)).distinct()
    except:
        frames = Frame.objects.none()
    return frames

def arg_regex_frames(frames, string):
    try:
        alternative_queries = []
        for alternative in string.split('|'):
            possible_frames = frames
            for conj in alternative.split('&'):
                model_results = []
                negation = False
                conj = conj.strip()
                if conj.startswith('!'):
                    conj = conj.lstrip('!')
                    negation = True
                regex = ur'^%s$' % escape_regex(conj)
                model_results = Argument.objects.filter(positions__frames__lemmas__old=False,
                                                        text_rep__regex=regex).distinct()
                if model_results.exists():
                    if negation:
                        possible_frames = possible_frames.exclude(positions__arguments__in=model_results)
                    else:
                        possible_frames = possible_frames.filter(positions__arguments__in=model_results)
                elif not model_results.exists() and not negation:
                    possible_frames = Frame.objects.none()
            alternative_queries.append(Q(id__in=possible_frames))
        frames = frames.filter(reduce(operator.or_, alternative_queries)).distinct()
    except:
        frames = Frame.objects.none()
    return frames

def filter_by_frames(lemmas, filter_rules):
    frames = SemanticFrame.objects.filter(next__isnull=True, removed=False)
    if filter_rules['frame_opinion']:
        frames = frames.filter(opinion=filter_rules['frame_opinion'])
    if filter_rules['sem_arguments']:
        frames = get_frames_by_args_rule(frames, filter_rules['sem_arguments'])
    if filter_rules['frame_opinion'] or filter_rules['sem_arguments']:
        lemmas = lemmas.filter(entry_obj__meanings__frames__in=frames).distinct()
    return lemmas

def get_frames_by_args_rule(frames, args_filter_rule):
    matching_frames = []
    for alternative in args_filter_rule:
        alt_matching_frames = get_matching_frames(frames, alternative)
        matching_frames.extend(alt_matching_frames.values_list('id', flat=True))
    return frames.filter(id__in=list(set(matching_frames)))

def get_matching_frames(frames, arguments_rules):
    for rules in arguments_rules:
        if not rules['negation']:
            frames = frames.filter(complements__in=matching_complements(rules))
        else:
            frames = frames.exclude(complements__in=matching_complements(rules))
    return frames
        
def matching_complements(filter_rules):
    complements = Complement.objects
    if filter_rules['role']:
        complements = complements.filter(roles=filter_rules['role'])
    if filter_rules['attribute']:
        complements = complements.filter(roles=filter_rules['attribute'])
    if filter_rules['general_prefs'] or filter_rules['synset_prefs'] or filter_rules['relational_prefs']:
        complements = complements.filter(selective_preference__isnull=False)
    if filter_rules['general_prefs']:
        complements = filter_by_general_prefs(complements, filter_rules['general_prefs'])
    if filter_rules['synset_prefs']:
        complements = filter_by_synset_prefs(complements, filter_rules['synset_prefs'])
    if filter_rules['relational_prefs']:
        complements = filter_by_relational_prefs(complements, filter_rules['relational_prefs'])
    return complements.all()

def filter_by_general_prefs(complements, prefs):
    complements = complements.exclude(selective_preference__generals=None)
    for pref in list(set(prefs)):
        if pref:
            complements = complements.filter(selective_preference__generals=pref)
    return complements

def filter_by_synset_prefs(complements, prefs):
    complements = complements.exclude(selective_preference__synsets=None)
    for pref in list(set(prefs)):
        if pref:
            try:
                pref_parts = pref.split('-')
                base = pref_parts[0]
                sense = pref_parts[1]
                synset = LexicalUnit.objects.get(base=base, sense=sense).synset
                complements = complements.filter(selective_preference__synsets=synset)
            except:
                complements = Complement.objects.none()
    return complements

def filter_by_relational_prefs(complements, prefs):
    complements = complements.exclude(selective_preference__relations=None)
    for pref in prefs:
        if pref['relation'] or pref['role'] or pref['attribute']:
            relational_prefs = RelationalSelectivePreference.objects
            if pref['relation']:
                relational_prefs = relational_prefs.filter(relation=pref['relation'])
            if pref['role'] or pref['attribute']:
                to_complements = Complement.objects
                if pref['role']:
                    to_complements = to_complements.filter(roles=pref['role'])
                if pref['attribute']:
                    to_complements = to_complements.filter(roles=pref['attribute'])
                relational_prefs = relational_prefs.filter(to__in=to_complements.all()).distinct()
            complements = complements.filter(selective_preference__relations__in=relational_prefs).distinct()
    return complements