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

import operator

from django.db.models import Q
from django.db.models import Count, Max

from accounts.models import RealizedLemma, RealizedPhraseology, RealizedSemantics
from common.decorators import render, ajax, AjaxError
from dictionary.common_func import frame_structure_exists
from dictionary.models import Configuration, Frame, Lemma, Lemma_Status, StatusChange
from semantics.change_log import backup_lemma_and_get_frames
from semantics.models import SemanticFrame
from semantics.utils import get_frames_differences


@render('lemma_status.html')
@ajax(method='get', encode_result=False)
def get_lemma_status(request, id):  
    selected_lemma = Lemma.objects.get(id=id) 
    abort_status = None
    next_statuses = []
    pos = selected_lemma.entry_obj.pos
  
    if ((selected_lemma.owner == request.user 
         and selected_lemma.status.type.sym_name != 'checked') 
        or request.user.has_perm('dictionary.confirm_lemma') 
        or (selected_lemma.vocabulary.editors.filter(id=request.user.id).exists() 
         and request.user.has_perm('dictionary.change_lemmas') 
         and selected_lemma.status.type.sym_name == 'initial')
        or phraseologic_status_changes(request.user, selected_lemma)
        or semantic_status_changes(request.user, selected_lemma)):    
        if request.user.groups.filter(group_settings__abort_statuses=selected_lemma.status.abort_status).exists():             
            abort_status = selected_lemma.status.abort_status
        next_statuses = selected_lemma.status.next_statuses
        if not lemma_can_be_temporary(selected_lemma):
            next_statuses = next_statuses.exclude(status=u'zalążkowe')
        next_statuses = next_statuses.filter(pk__in=request.user.groups.all()[0].group_settings.next_statuses.all()) 
        next_statuses = next_statuses.all()
            
    return {'lemma': selected_lemma,
            'abort_status': abort_status,
            'next_statuses': next_statuses,
            'pos': pos,
            'status_changes': selected_lemma.status_history.order_by('-date')}

def phraseologic_status_changes(user, selected_lemma):
    phraseologic_change = False
    if (user.has_perm('dictionary.add_phraseologic_frames') and 
        selected_lemma.status.type.sym_name != 'checked_f' and 
        selected_lemma.vocabulary.editors.filter(id=user.id).exists() and 
        (selected_lemma.phraseologist == user or selected_lemma.status.type.sym_name == 'checked')):
        phraseologic_change = True
    return phraseologic_change

def semantic_status_changes(user, selected_lemma):
    semantic_change = False
    if (user.has_perm('dictionary.add_semantic_frames') and 
        selected_lemma.status.type.sym_name != 'checked_s' and 
        selected_lemma.vocabulary.editors.filter(id=user.id).exists() and 
        (selected_lemma.semanticist == user or selected_lemma.status.type.sym_name == 'checked_f')):
        semantic_change = True
    return semantic_change

def lemma_can_be_temporary(lemma):
    can_be_temporary = False
    system_conf = Configuration.objects.get(selected_conf=True)
    if (lemma.frequency_1M < system_conf.min_1M_freq and 
        lemma.frequency_300M < system_conf.min_300M_freq and 
        lemma.skladnica_frames.exists()):
        can_be_temporary = True
    return can_be_temporary

@ajax(method='post')
def status_need_validation(request, status_id, lemma_id):
    next_status = False
    lemma_status = Lemma.objects.get(id=lemma_id).status
    new_status = Lemma_Status.objects.get(id=status_id)
    if(new_status.priority > lemma_status.priority):
        next_status = True 
    need_validation = (new_status.validate or 
                       new_status.check_examples or
                       new_status.check_semantics) and next_status
    return {'need_validation': need_validation}   

@ajax(method='post')
def lemma_status_change(request, status_id, lemma_id):
    if not request.user.is_authenticated():
        raise AjaxError('user logged out')
    
    try:
        lemma_obj = Lemma.objects.get(id=lemma_id, old=False)
    except Lemma.DoesNotExist:
        raise AjaxError('old version')
    entry_obj = lemma_obj.entry_obj
    # sprawdza czy uzytkownik moze modyfikowac hasla w danym slowniku
    try:
        lemma_obj.vocabulary.editors.get(username=request.user.username)
        edit_vocabulary = True
    except:
        edit_vocabulary = False
        
    changed = False
    message = ''  
    new_status = None
    try: # jesli zarezerowano haslo przyciskiem
        new_status = Lemma_Status.objects.get(id=status_id)
    except:
        pass
    
    if new_status and new_status == lemma_obj.status:
        raise AjaxError('already changed')
    
    if(new_status and 
       not new_status == lemma_obj.status.abort_status and 
       not lemma_obj.status.next_statuses.filter(pk=new_status.pk).exists()):
        raise AjaxError('wrong change')

    actual_semantic_frames = SemanticFrame.objects.none()
    next_status = False
    if(new_status):
        actual_semantic_frames = backup_lemma_and_get_frames(lemma_obj)
    
    if(new_status and new_status.priority > lemma_obj.status.priority):
        next_status = True
    # reserve lemma
    if(lemma_obj.status.type.sym_name == 'initial' and edit_vocabulary and 
       request.user.has_perm('dictionary.change_lemmas')):
        lemma_obj.owner = request.user
        changed = True
        if not new_status:
            new_status = lemma_obj.status.next_statuses.order_by('priority')[0]
    # resign from lemma
    elif(not next_status and
         new_status and new_status.type.sym_name == 'initial'):
        lemma_obj.owner = None
        changed = True     
    # lemma type changed to ready or temporary
    elif(new_status and new_status.type.sym_name == 'ready' and next_status):
        if entry_obj.pos.tag == 'verb':
            flat_frames_value = 4.5
        else:
            flat_frames_value = 3.5
        update_lemma_stats_ready(lemma_obj, lemma_obj.owner, new_status, flat_frames_value)
        changed = True
    # lemma type changed to checked
    elif(new_status and new_status.type.sym_name == 'checked' and next_status):
        if lemma_obj.owner == request.user:
            if lemma_obj.status.abort_status != new_status:
                message = u'Nie można zatwierdzać hasła, którego jest się właścicielem.' 
        else:
            if lemma_obj.status.abort_status != new_status:
                if entry_obj.pos.tag == 'verb':
                    checked_frame_value = 1.0
                    corrected_frame_value = 7.0
                    bonus_factor = 1.2
                else:
                    checked_frame_value = 1.0
                    corrected_frame_value = 6.0
                    bonus_factor = 1.0
                update_lemma_stats_conf(lemma_obj, lemma_obj.owner, request.user, new_status,
                                        checked_frame_value, corrected_frame_value, bonus_factor)
            changed = True
    # zmiana statusu na w obrobce
    elif(new_status and 
         (lemma_obj.status.type.sym_name == 'ready' or lemma_obj.status.type.sym_name == 'checked') and 
         lemma_obj.status.abort_status == new_status):
        remove_lexicography_payments(lemma_obj)
        changed = True
        
    # pobieranie hasla do obrobki frazeologicznej
    elif(new_status and 
         lemma_obj.status.type.sym_name == 'checked' and 
         edit_vocabulary and new_status.type.sym_name == 'edit_f' 
         and next_status):
        lemma_obj.phraseologist = request.user
        add_new_frames_to_phraseologic_propositions(lemma_obj)
        changed = True
    # porzucanie obrobki frazeologicznej hasla
    elif(new_status and 
         lemma_obj.status.type.sym_name == 'edit_f' and 
         lemma_obj.status.abort_status == new_status):
        lemma_obj.phraseologist = None
        changed = True
    # zmiana statusu hasla na gotowe frazeologicznie
    elif(new_status and new_status.type.sym_name == 'ready_f' 
         and next_status):
        new_phraseologic_frame_value = 7.0
        proposed_phraseologic_frame_value = 2.0
        update_lemma_stats_ready_f(lemma_obj, lemma_obj.phraseologist, 
                                   new_status, new_phraseologic_frame_value,
                                   proposed_phraseologic_frame_value)
        add_new_frames_to_phraseologic_propositions(lemma_obj)
        changed = True
    # zmiana statusu hasla na sprawdzone frazeologicznie
    elif(new_status and new_status.type.sym_name == 'checked_f' 
         and next_status):
        checked_frame_value = 4.0
        corrected_frame_value = 7.0
        bonus = 0.5
        update_lemma_stats_conf_f(lemma_obj, lemma_obj.phraseologist, request.user, new_status,
                                  checked_frame_value, corrected_frame_value, bonus)
        add_new_frames_to_phraseologic_propositions(lemma_obj)
        changed = True
        
    # pobieranie hasla do obrobki semantycznej
    elif(new_status and 
         lemma_obj.status.type.sym_name == 'checked_f' and 
         edit_vocabulary and new_status.type.sym_name == 'edit_s' 
         and next_status):
        lemma_obj.semanticist = request.user
        add_new_frames_to_phraseologic_propositions(lemma_obj)
        changed = True
    # porzucanie obrobki semantycznej hasla
    elif(new_status and 
         lemma_obj.status.type.sym_name == 'edit_s' and 
         lemma_obj.status.abort_status == new_status):
        lemma_obj.semanticist = None
        changed = True
    # zmiana statusu hasla na gotowe semantycznie
    elif(new_status and new_status.type.sym_name == 'ready_s' 
         and next_status):
        ### naliczanie oplat za gotowosc semantyczna
        frame_value = 8.0
        update_sem_stats_ready_s(lemma_obj.entry_obj, actual_semantic_frames, 
                                 lemma_obj.semanticist, new_status, frame_value)
        add_new_frames_to_phraseologic_propositions(lemma_obj)
        changed = True
    # zmiana statusu hasla na sprawdzone semantycznie
    elif(new_status and new_status.type.sym_name == 'checked_s' 
         and next_status):
        checked_frame_value = 0.0
        corrected_frame_value = 0.0
        bonus = 4.0
        part_bonus = 2.0
        connection_bonus = 0.1
        ### naliczanie oplat za sprawdzenie i bonusow
        update_sem_stats_conf_s(entry=lemma_obj.entry_obj, 
                                semantic_frames=actual_semantic_frames, 
                                semanticist=lemma_obj.semanticist, 
                                supersemanticist=request.user, 
                                status=new_status,
                                checked_frame_value=checked_frame_value, 
                                corrected_frame_value=corrected_frame_value, 
                                bonus_factor=bonus, 
                                part_bonus_factor=part_bonus,
                                connection_bonus=connection_bonus)
        
        add_new_frames_to_phraseologic_propositions(lemma_obj)
        changed = True    
    # zmiana statusu na w obrobce semantycznej
    elif(new_status and 
         (lemma_obj.status.type.sym_name == 'ready_s' or lemma_obj.status.type.sym_name == 'checked_s') and 
         lemma_obj.status.abort_status == new_status):
        remove_semantic_payments(lemma_obj.entry_obj)
        changed = True
    
    # jak oznaczamy status jako "do usuniecia", to czyscimy oplaty za haslo
    elif(new_status and new_status.type.sym_name == 'erase' 
         and next_status):
        changed = True
        erase_payments(lemma_obj)
    
    elif(new_status):
        changed = True
    
    if changed and new_status != lemma_obj.status: 
        lemma_obj.status = new_status
        lemma_obj.save()
        status_change = StatusChange(act_owner=lemma_obj.owner,
                                     changer=request.user,
                                     lemma=lemma_obj,
                                     status=new_status)
        status_change.save()
        status_change.semantic_frames.add(*actual_semantic_frames.all())
        lemma_obj.status_history.add(status_change)    
        
    if new_status:
        new_status_type = new_status.type.sym_name
    else:
        new_status_type = ''         
         
    return {'entry'          : lemma_obj.entry,
            'lemma_id'       : lemma_obj.id,
            'changed'        : changed,
            'message'        : message,
            'new_status_type': new_status_type,
            'next_status'    : next_status}

############# marking as 'to erase' #################   
def erase_payments(lemma):
    RealizedLemma.objects.filter(lemma__entry_obj=lemma.entry_obj).delete()
    RealizedPhraseology.objects.filter(lemma__entry_obj=lemma.entry_obj).delete()
    RealizedSemantics.objects.filter(entry=lemma.entry_obj).delete()                          

############## lexicography #######################           
def update_lemma_stats_ready(lemma, lex, status, flat_frames_value):
    lex_dict = {'made_frames': 0,
                'cash': 0.0}
    for frame in lemma.frames.all():
        flat_frames = float(frame.positions.annotate(num_args=Count('arguments')).aggregate(Max('num_args'))['num_args__max'])
        lex_dict['made_frames'] += flat_frames
        lex_dict['cash'] += flat_frames_value*flat_frames   
    
    lex_real_lemma = RealizedLemma(lemma=lemma, cash=lex_dict['cash'],
                                   made_frames=lex_dict['made_frames'],
                                   paid=False, status=status, bonus=False,
                                   counted=False)
    lex_real_lemma.save()
    lex.user_stats.lemma_real_history.add(lex_real_lemma)

def update_lemma_stats_conf(lemma, lex, superlex, status,
                               checked_frame_value, corrected_frame_value, bonus_factor):
    ready_statuses = Lemma_Status.objects.filter(type__sym_name='ready')
    q_ready_statuses = [Q(status=ready_status) for ready_status in ready_statuses.all()]
    
    all_realized_lemmas = RealizedLemma.objects.filter(reduce(operator.or_, q_ready_statuses))
    ready_lemma = all_realized_lemmas.get(lemma__entry_obj=lemma.entry_obj).lemma
    lex_dict = {'same_frames': 0,
                'wrong_frames': 0,
                'cash': 0.0}
    superlex_dict = {'same_frames': [],
                     'redo_frames': [],
                     'cash': 0.0}
    q_same_frames = []
    for frame in ready_lemma.frames.all():
        flat_frames = float(frame.positions.annotate(num_args=Count('arguments')).aggregate(Max('num_args'))['num_args__max'])
        try:          
            same_frame = lemma.frames.get(text_rep=frame.text_rep)
            
            superlex_dict['same_frames'].append(frame)
            superlex_dict['cash'] += checked_frame_value
            
            lex_dict['same_frames'] += flat_frames
            bonus = flat_frames * bonus_factor*flat_frames**(1.0/3.0)
            lex_dict['cash'] += bonus
            
            q_same_frames.append(Q(text_rep=same_frame.text_rep))
        except Frame.DoesNotExist:
            lex_dict['wrong_frames'] += flat_frames
            continue
    
    new_frames = lemma.frames
    if len(q_same_frames) > 0:
        new_frames = new_frames.exclude(reduce(operator.or_, q_same_frames))
    superlex_dict['redo_frames'] = new_frames.all()
    for frame in superlex_dict['redo_frames']:
        superlex_dict['cash'] += corrected_frame_value
    # superleksykograf nie zostal jeszcze oplacony za dane haslo, zmien wartosc

    superlex_real_lemma = RealizedLemma(lemma=lemma, cash=superlex_dict['cash'],
                                        corr_frames=len(superlex_dict['redo_frames']),
                                        ncorr_frames=len(superlex_dict['same_frames']),
                                        paid=False, status=status, bonus=False,
                                        counted=False)
    superlex_real_lemma.save()
    superlex.user_stats.lemma_real_history.add(superlex_real_lemma)
    
    # hasla pochodzace z cesara maja cash = 0.0 i nie nalezy ich bonusować
    try:
        RealizedLemma.objects.get(lemma__entry=lemma.entry, 
                                  status__type__sym_name='ready', 
                                  bonus=False, cash=0.0, paid=True,
                                  counted=True)
    except RealizedLemma.DoesNotExist:
        lex_real_lemma = RealizedLemma(lemma=lemma, 
                                       cash=lex_dict['cash'],
                                       prop_frames=lex_dict['same_frames'],
                                       wrong_frames=lex_dict['wrong_frames'],
                                       paid=False, status=status,
                                       bonus=True, counted=False)
        lex_real_lemma.save()
        lex.user_stats.lemma_real_history.add(lex_real_lemma)
        
def remove_lexicography_payments(lemma):
    RealizedLemma.objects.filter(lemma__entry_obj=lemma.entry_obj).delete()
                
######################## phraseology #############################
def add_new_frames_to_phraseologic_propositions(lemma):
    entry_obj = lemma.entry_obj
    phraseologic_frames = lemma.frames.filter(phraseologic=True).all()
    phraseologic_propositions = entry_obj.phraseologic_propositions.all()
    propositions_to_add = []
    for frame in phraseologic_frames:
        if not frame_structure_exists(frames=phraseologic_propositions, 
                                      searched_frame=frame):
            propositions_to_add.append(frame)
    entry_obj.phraseologic_propositions.add(*propositions_to_add)

def update_lemma_stats_ready_f(lemma, phraseologist, status, 
                                  new_phraseologic_frame_value, proposed_phraseologic_frame_value):
    phraseologist_dict = {'new_frames': 0,
                          'reused_frames': 0,
                          'cash': 0.0}
    for frame in lemma.frames.filter(phraseologic=True).all():
        if frame_structure_exists(frames=lemma.entry_obj.phraseologic_propositions.all(), 
                                  searched_frame=frame):
            phraseologist_dict['reused_frames'] += 1
            phraseologist_dict['cash'] += proposed_phraseologic_frame_value
        else:
            phraseologist_dict['new_frames'] += 1
            phraseologist_dict['cash'] += new_phraseologic_frame_value 
    
    phrase_real_lemma = RealizedPhraseology(lemma=lemma, 
                                            cash=phraseologist_dict['cash'],
                                            new_frames=phraseologist_dict['new_frames'],
                                            reused_frames=phraseologist_dict['reused_frames'],
                                            paid=False, 
                                            status=status, 
                                            bonus=False,
                                            counted=False)
    phrase_real_lemma.save()
    phraseologist.user_stats.phraseology_real_history.add(phrase_real_lemma)
                              
def update_lemma_stats_conf_f(lemma, phraseologist, superphraseologist, status,
                                 checked_frame_value, corrected_frame_value, bonus):
    ready_statuses = Lemma_Status.objects.filter(type__sym_name='ready_f')
    q_ready_statuses = [Q(status=ready_status) for ready_status in ready_statuses.all()]
    
    ready_lemmas = RealizedPhraseology.objects.filter(reduce(operator.or_, q_ready_statuses))
    ready_lemma = ready_lemmas.get(lemma__entry_obj=lemma.entry_obj).lemma
    phraseologist_dict = {'same_frames': 0,
                          'wrong_frames': 0,
                          'cash': 0.0}
    superphraseologist_dict = {'same_frames': [],
                               'redo_frames': [],
                               'cash': 0.0}
    q_same_frames = []
    for frame in ready_lemma.frames.filter(phraseologic=True).all():
        try:          
            same_frame = lemma.frames.filter(phraseologic=True).get(text_rep=frame.text_rep)
            
            superphraseologist_dict['same_frames'].append(frame)
            superphraseologist_dict['cash'] += checked_frame_value
            
            phraseologist_dict['same_frames'] += 1
            phraseologist_dict['cash'] += bonus
            
            q_same_frames.append(Q(text_rep=same_frame.text_rep))
        except Frame.DoesNotExist:
            phraseologist_dict['wrong_frames'] += 1
            continue
    
    new_frames = lemma.frames.filter(phraseologic=True)
    if len(q_same_frames) > 0:
        new_frames = new_frames.exclude(reduce(operator.or_, q_same_frames))
    superphraseologist_dict['redo_frames'] = new_frames.all()
    for frame in superphraseologist_dict['redo_frames']:
        superphraseologist_dict['cash'] += corrected_frame_value

    superphraseologist_real_lemma = RealizedPhraseology(lemma=lemma, 
                                                        cash=superphraseologist_dict['cash'],
                                                        corr_frames=len(superphraseologist_dict['redo_frames']),
                                                        ncorr_frames=len(superphraseologist_dict['same_frames']),
                                                        paid=False, 
                                                        status=status, 
                                                        bonus=False,
                                                        counted=False)
    superphraseologist_real_lemma.save()
    superphraseologist.user_stats.phraseology_real_history.add(superphraseologist_real_lemma)
        
    phraseologist_real_lemma = RealizedPhraseology(lemma=lemma, 
                                                   cash=phraseologist_dict['cash'],
                                                   prop_frames=phraseologist_dict['same_frames'],
                                                   wrong_frames=phraseologist_dict['wrong_frames'],
                                                   paid=False, 
                                                   status=status,
                                                   bonus=True, 
                                                   counted=False)
    phraseologist_real_lemma.save()
    phraseologist.user_stats.phraseology_real_history.add(phraseologist_real_lemma)

####################### semantics #############################     
def update_sem_stats_ready_s(entry, semantic_frames, semanticist, status, frame_value):
    actual_frames_count = semantic_frames.count()
    sem_dict = {'made_frames': actual_frames_count,
                'cash': frame_value*float(actual_frames_count)}

    realized_semantics = RealizedSemantics(entry=entry, cash=sem_dict['cash'],
                                           made_frames=sem_dict['made_frames'],
                                           status=status, bonus=False)
    realized_semantics.save()
    realized_semantics.frames.add(*semantic_frames.all())
    semanticist.user_stats.semantics_real_history.add(realized_semantics)

def update_sem_stats_conf_s(entry, semantic_frames, semanticist, supersemanticist, status,
                               checked_frame_value, corrected_frame_value, 
                               bonus_factor, part_bonus_factor, connection_bonus):
    ready_statuses = Lemma_Status.objects.filter(type__sym_name='ready_s')
    q_ready_statuses = [Q(status=ready_status) for ready_status in ready_statuses.all()]
        
    ready_semantics = RealizedSemantics.objects.filter(reduce(operator.or_, q_ready_statuses))
    ready_sem_frames= ready_semantics.get(entry=entry).frames
    checked_sem_frames = semantic_frames
    ready_to_checked_diffs = get_frames_differences(ready_sem_frames.all(), checked_sem_frames.all())
    checked_to_ready_diffs = get_frames_differences(checked_sem_frames.all(), ready_sem_frames.all())
    
    connections_amount = count_connections(ready_to_checked_diffs)
    sem_cash = (bonus_factor*float(len(ready_to_checked_diffs['matching_frames'])) +
                part_bonus_factor*float(len(ready_to_checked_diffs['part_matching_frames'])) +
                connection_bonus*float(connections_amount))
    sem_dict = {'same_frames': len(ready_to_checked_diffs['matching_frames']),
                'part_same_frames': len(ready_to_checked_diffs['part_matching_frames']),
                'wrong_frames': len(ready_to_checked_diffs['missing_frames']),
                'added_connections': connections_amount,
                'cash': sem_cash}
    
    supersem_cash = (float(len(checked_to_ready_diffs['missing_frames'])+len(checked_to_ready_diffs['part_matching_frames']))*corrected_frame_value +
                     float(len(ready_to_checked_diffs['matching_frames']))*checked_frame_value)
    supersem_dict = {'same_frames': len(checked_to_ready_diffs['matching_frames']),
                     'part_same_frames': len(checked_to_ready_diffs['part_matching_frames']),
                     'redo_frames': len(checked_to_ready_diffs['missing_frames']),
                     'cash': supersem_cash}
     
    supersem_real_semantics = RealizedSemantics(entry=entry, 
                                                cash=supersem_dict['cash'],
                                                corr_frames=supersem_dict['redo_frames'],
                                                part_corr_frames=supersem_dict['part_same_frames'],
                                                ncorr_frames=supersem_dict['same_frames'],
                                                status=status, 
                                                bonus=False)
    supersem_real_semantics.save()
    supersem_real_semantics.frames.add(*semantic_frames.all())
    supersemanticist.user_stats.semantics_real_history.add(supersem_real_semantics)
                
    sem_real_semantics = RealizedSemantics(entry=entry, 
                                           cash=sem_dict['cash'],
                                           prop_frames=sem_dict['same_frames'],
                                           part_prop_frames=sem_dict['part_same_frames'],
                                           wrong_frames=sem_dict['wrong_frames'],
                                           added_connections=sem_dict['added_connections'],
                                           status=status,
                                           bonus=True)
    sem_real_semantics.save()
    sem_real_semantics.frames.add(*semantic_frames.all())
    semanticist.user_stats.semantics_real_history.add(sem_real_semantics)
    
def count_connections(differences):
    amount = 0 
    for frame in differences['matching_frames']:
        amount += frame.connected_schemata().count()
    for frame in differences['part_matching_frames']:
        amount += frame.connected_schemata().count()
    return amount

def remove_semantic_payments(entry):
    RealizedSemantics.objects.filter(entry=entry).delete()