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

from semantics.models import SemanticRole, SemanticFrame, Complement, \
                             LexicalUnit, FrameRankings, SemanticRolesDisplay, \
                             LexicalUnitExamples, SelectivePreferenceRelations, \
                             GeneralSelectivePreference, FrameOpinion

from dictionary.models import Frame_Char_Model, Lemma, Lemma_Status, \
                              sort_arguments, sort_positions
from dictionary.ajax_lemma_view import user_can_modify
from django.core.exceptions import SuspiciousOperation
from django.core.urlresolvers import reverse
from django.db.models import Q

from common.decorators import render, ajax
from semantics.saving import modify_frames, update_meanings
from semantics.validation import validate_schemas, validate_frames, validate_lexical_units


####################################### render #####################################

@render('semantics.html')
@ajax(method='get', encode_result=False)
def ajax_semantics(request, id):
    context = {}
    lemma = Lemma.objects.get(id=id)
    context['lemma'] = lemma
    context['can_modify'] = (user_can_modify(lemma, request.user) and 
                             lemma.status.stage_of_work.sym_name == 'semantics')
    context['js_vars'] = {
        'ajax_frames': reverse('ajax_frames'),
        'ajax_schemas': reverse('ajax_schemas'),
        'ajax_units': reverse('ajax_units'),
        'ajax_roles': reverse('ajax_roles'),
        'ajax_opinions': reverse('ajax_opinions'),
        'ajax_create_frame': reverse('ajax_create_frame'),
        'ajax_create_complement': reverse('ajax_create_complement'),
        'ajax_examples': reverse('ajax_examples'),
        'ajax_update_meanings': reverse('ajax_update_meanings'),
        'ajax_modify_frames': reverse('ajax_modify_frames'),
        'ajax_synsets': reverse('ajax_synsets'),
        'ajax_relations': reverse('ajax_relations'),
        'ajax_predefined_preferences': reverse('ajax_predefined_preferences'),
        'ajax_plWN_context_lookup': reverse('ajax_plWN_context_lookup'),
    }
    return context

def reorder_history(frames_list):
    next_ids = set()
    for frame in frames_list:
        if frame.next is not None:
            next_ids.add(frame.next.id)
    
    first = set()
    for frame in frames_list:
        if frame.id not in next_ids:
            first.add(frame)
            
    result = []
    for frame in first:
        current = frame
        while current is not None:
            result.append(current)
            current = current.next
            
    result.reverse()
    return result


@render('frames.json')
@ajax(method='get', encode_result=False)
def ajax_frames(request, lemma_id):

    lemma = Lemma.objects.get(id=lemma_id, old=False)
    lexical_units = LexicalUnit.objects.filter(Q(base__startswith=lemma.entry + u' ')|Q(base__contains=u' '+lemma.entry+u' ')|Q(base__endswith=u' '+lemma.entry)|Q(base=lemma.entry)).order_by('sense')
    
    alternations = {}        
    frames_dict = {}
    frame_units = {}
    for lexical_unit in lexical_units:
        frames = lexical_unit.actual_frames()
        for frame in frames:
            alternations[frame.id] = {}
            frames_dict[frame.id] = frame
            if frame.id not in frame_units:
                frame_units[frame.id] = []
            frame_units[frame.id].append(lexical_unit)
            
    type_frames = {}
    for frame_id in frame_units:
        t = tuple(frame_units[frame_id])
        if t not in type_frames:
            type_frames[t] = []
        type_frames[tuple(frame_units[frame_id])].append(frames_dict[frame_id])

#    ala[ma]=kot    
        
    frames_display = []
    complement_arguments = {}
    arguments_frame_connected = {}
    
    for t in type_frames:
        type_frames[t] = reorder_history(type_frames[t])
    
    for t in type_frames:
        frame_display = {"lexical_units": [], "frames": []}
        for lu in list(t):
            frame_display["lexical_units"].append({"id": str(lu.id), "base": lu.base, "sense": str(lu.sense)})
        for frame in type_frames[t]:
            # frame_complements = Complement.objects.filter(frame=frame)
            frame_complements = frame.complements.all()
        
            # classes and first frame/position part of identifiers
            frame_ids = [u'frame_' + str(frame.id) + '_comp_' + str(complement.id) + '_' for complement in frame_complements]

            # names of roles
            frame_roles = [{'csv_id': i, 'csv_class': c, 'argument': a} for i, c, a in zip(frame_ids, frame_ids, [[role.id for role in complement.roles.all()] for complement in frame_complements])]
            
            # rowspan for 'Argumenty:'
            # frame_preferences_rowspan = max(max([len(preference) for preference in [complement.selective_preference.all() for complement in frame_complements]] + [0]), 1)
            frame_preferences_rowspan = max(max([len(complement.selective_preference.generals.all()) + len(complement.selective_preference.synsets.all()) + len(complement.selective_preference.relations.all()) for complement in frame_complements if complement.selective_preference is not None] +[0]), 1)
            
            frame_preferences = []
            for i in range(frame_preferences_rowspan):
                row = []
                idents = []
                for frame_id, complement in zip(frame_ids, frame_complements):
                    if complement.selective_preference is not None:
                        generals = complement.selective_preference.generals.all()
                        synsets = complement.selective_preference.synsets.all()
                        relations = complement.selective_preference.relations.all()
                        synset_relations = complement.selective_preference.synset_relations.all()
                        generals_len = len(generals)
                        synsets_len = len(synsets)
                        relations_len = len(relations)
                        synset_relations_len = len(synset_relations)
                        if generals_len > i:
                            general = generals[i]
                            row.append(general.name)
                            idents.append(frame_id + 'pref_g' + str(general.id) + '_')
                        elif generals_len + synsets_len > i:
                            synset = synsets[i - generals_len]
                            # lexical_units_preference = LexicalUnit.objects.filter(synset=synset)
                            row.append(unicode(synset))
                            idents.append(frame_id + 'pref_s' + str(synset.id) + '_')
                        elif generals_len + synsets_len + relations_len > i:
                            relation = relations[i - generals_len - synsets_len]
                            row.append(relation.relation.name + '<br /> -> [' + ', '.join([r.role for r in relation.to.roles.all()]) + ']')
                            idents.append(frame_id + 'pref_r' + str(relation.id) + '_')
                        elif generals_len + synsets_len + relations_len + synset_relations_len > i:
                            relation = relations[i - generals_len - synsets_len - relations_len]
                            synset = relation.to
                            row.append(relation.relation.name + '<br /> -> [' + unicode(synset) + ']')
                            idents.append(frame_id + 'pref_R' + str(relation.id) + '_')
                        else:
                            row.append('')
                            idents.append(frame_id + 'pref_-' + str(i + 1) + '_')
                    else:
                        row.append('')
                        idents.append(frame_id + 'pref_-' + str(i + 1) + '_')
                frame_preferences.append([{'csv_id': i, 'csv_class': c, 'preference': p} for i, c, p in zip(idents, frame_ids, row)])
            
            display = {"roles": frame_roles, "preferences": frame_preferences}
            if frame.opinion is None:
                status = u'brak'
            else:
                status = frame.opinion.value
            frame_display["frames"].append({"frame_id": str(frame.id), "colspan": str(max(len(frame_roles), 1)), "rowspan": str(frame_preferences_rowspan), "status": status, "display": display})
            
            for complement, complement_class in zip(frame_complements, frame_ids):
                if complement_class not in complement_arguments:
                    complement_arguments[complement_class] = []
                
                for schema_position in complement.realizations.all():
                    schema = schema_position.frame
                    position = schema_position.position
                    argument = schema_position.argument
                    alternation = schema_position.alternation
                    realization_id = u'schema_' + str(schema.id) + u'_pos_' + str(position.id) + '_arg_' + str(argument.id) + '_' + 'alt_' + str(alternation) + '_'
                    complement_arguments[complement_class].append(realization_id)
                    if realization_id not in arguments_frame_connected:
                        arguments_frame_connected[realization_id] = []
                    arguments_frame_connected[realization_id].append('frame_' + str(frame.id) + '_')
                    if schema.id in alternations[frame.id]:
                        alternations[frame.id][schema.id] = max(alternations[frame.id][schema.id], alternation)
                    else:
                        alternations[frame.id][schema.id] = alternation
                    # alternations[frame.id] = {}
        

        frames_display.append(frame_display)

    # ala["ma"] = "kot"

    context = {
               'frames_display': frames_display, 
               'connections': {'connected': complement_arguments, 'connected_reverse': arguments_frame_connected},
               'frames_count': lemma.entry_obj.actual_frames().count(),
               'alternations': alternations
              }

    return context

@render('units.json')
@ajax(method='get', encode_result=False)   
def ajax_units(request, lemma_id):
    lemma = Lemma.objects.get(id=lemma_id, old=False)
#    lexical_units = LexicalUnit.objects.filter(Q(base = lemma.entry, pos="czasownik")|Q(base = lemma.entry + u' się', pos="czasownik")).order_by('base', 'sense')
    lexical_units = LexicalUnit.objects.filter(Q(base__startswith=lemma.entry + u' ', pos="czasownik")|Q(base__contains=u' '+lemma.entry+u' ', pos="czasownik")|Q(base__endswith=u' '+lemma.entry, pos="czasownik")|Q(base=lemma.entry, pos="czasownik")).order_by('base', 'sense')

    context = {
               'lexical_units': [{"id": lu.id, "luid": lu.luid, "base": lu.base, "sense": lu.sense, "pos": lu.pos, "glossa": lu.glossa, "definition": lu.definition} for lu in lexical_units],
               'informations': {'base': lemma.entry, 'sense': max(['A'] + [chr(ord(lu.sense) + 1) for lu in lexical_units.filter(luid=-1)])}, # TODO: 2 different free senses for with/whthout 'się'
              }

    return context

@ajax(method='get')   
def ajax_predefined_preferences(request):
    predefined = []
    for preference in GeneralSelectivePreference.objects.order_by('name'):
        if preference.members:
            members = [member.name for member in preference.members.generals.order_by('name')]
            members.extend([unicode(synset) for synset in preference.members.synsets.all()])
            content = u'%s: (%s)' % (preference.name, ', '.join(members))
        else:
            content = u'%s' % (preference.name)
        predefined.append({"id": preference.id, "content": content})

    context = {
               'predefined': predefined,
              }

    return context

@render('synsets.json')
@ajax(method='get', encode_result=False)   
def ajax_synsets(request, base, pos):
    
    if pos == '_':
        lexical_units = LexicalUnit.objects.filter(base=base).order_by('pos', 'base', 'sense')
    else:
        lexical_units = LexicalUnit.objects.filter(base=base, pos=pos).order_by('pos', 'base', 'sense')
    
    synsets = []
    for representative in lexical_units:
        synset = [{"id": lu.id, "luid": lu.luid, "base": lu.base, "sense": lu.sense, "pos": lu.pos, "glossa": lu.definition} for lu in LexicalUnit.objects.filter(synset=representative.synset)]
        synsets.append({"id": representative.synset.id, "content": synset})

    context = {
               'synsets': synsets,
              }

    return context

@render('relations.json')
@ajax(method='get', encode_result=False)   
def ajax_relations(request):
    
    relations = [{"id": relation.id, "content": relation.name} for relation in SelectivePreferenceRelations.objects.all()]

    context = {
               'relations': relations,
              }

    return context

@render('opinions.json')  
@ajax(method='get', encode_result=False)
def ajax_opinions(request):
    
    opinions = [{"id": opinion.short, "name": opinion.value} for opinion in FrameOpinion.objects.all().order_by('priority')]
    
    context = {
               'opinions': opinions,
              }

    return context

@render('roles.json')  
@ajax(method='get', encode_result=False)
def ajax_roles(request):       
    roles_display = []
    maxrow = SemanticRolesDisplay.objects.all().order_by('-row')[0].row
    for i in range(1, maxrow + 1):
        row = []
        cells = SemanticRolesDisplay.objects.filter(row=i).order_by('column')
        for cell in cells:
            content = cell.roles.all().order_by('id')
            commas = [','] * max(len(content) - 1, 0) + ['']
            row.append((cell.caption, cell.rowspan, cell.colspan, zip(content, commas)))
        commas = [','] * max(len(row) - 1, 0) + ['']
        captions, rowspans, colspans, contents = zip(*row)
        roles_display.append(zip(list(captions), list(rowspans), list(colspans), list(contents), commas))
    commas = [','] * max(len(roles_display) - 1, 0) + ['']

    context = {
               'roles_display': zip(roles_display, commas),
              }

    return context


@render('schemas.json')   
@ajax(method='get', encode_result=False)
def ajax_schemas(request, lemma_id):

    lemma = Lemma.objects.get(id=lemma_id, old=False)
    
    schemas_all = lemma.frames.all()
    
    characteristics = {}
    characteristics_ids = []
    schemas_by_characteristic = {}
    for schema in schemas_all:
        chars = schema.characteristics.all()
        char_types = [c.type for c in chars]
        l = [(Frame_Char_Model.objects.get(model_name=c.type).priority, c) for c in chars]
        l.sort()
        _, chars = zip(*l)
        chars = list(chars)
        characteristic_id = tuple([char.id for char in chars])
        if characteristic_id not in characteristics:
            characteristics_ids.append(characteristic_id)
            values = [char.value.value for char in chars]
            if values[0]:
                characteristics[characteristic_id] = "%s (%s)" % (values[0], ','.join(values[1:]))
            else:
                characteristics[characteristic_id] = "(%s)" % (','.join(values[1:]))
            schemas_by_characteristic[characteristic_id] = []
        schemas_by_characteristic[characteristic_id].append(schema)
    
    lexical_units = LexicalUnit.objects.filter(Q(base=lemma.entry) | Q(base=lemma.entry+u' się'))

    schemas_display = []
    schema_unit_rank = {}
    for characteristic_id in characteristics_ids:
        schema_display = {"characteristic_id": '_'.join([str(i) for i in characteristic_id]), "characteristic_display": characteristics[characteristic_id], "schemas": []}
        schemas = schemas_by_characteristic[characteristic_id]
        
        for schema in schemas: # row by row display for schema tables
            ordered_positions = sort_positions(schema.positions.all())
            # classes and first frame/position part of identifiers
            schema_ids = [u'schema_' + str(schema.id) + '_pos_' + str(position.id) + '_' for position in ordered_positions]

            # names of categories (along with class names
            schema_categories = zip(schema_ids, [','.join([category.category for category in position.ordered_categories()]) for position in ordered_positions])
            
            # value of rowspan for 'Argumenty:' header
            schema_arguments_rowspan = max(max([position.arguments.count() for position in ordered_positions] + [0]), 1)
            
            display = {"categories": [{"csv_id": c, "csv_class": c, "argument": a} for c, a in schema_categories], "arguments": []}
            for i in range(schema_arguments_rowspan):
            
                row = []
                idents = []
                for schema_id, position in zip(schema_ids, ordered_positions):
                    if position.arguments.count() > i:
                        ordered_arguments = sort_arguments(position.arguments.all())
                        row.append(unicode(ordered_arguments[i]))
                        idents.append(schema_id + 'arg_' + str(ordered_arguments[i].id) + '_')
                    else: # this category has fewer posible argument realizations
                        row.append('')
                        idents.append(schema_id + 'arg_-' + str(i + 1) + '_')
                # identifier, class, argument
                display["arguments"].append([{"csv_id": i, "csv_class": c, "argument": a} for i, c, a in zip(idents, schema_ids, row)])
            
            schema_display["schemas"].append({"schema_id": str(schema.id), "grade": lemma.get_schema_opinion(schema), "colspan": str(max(len(schema_categories), 1)), "rowspan": str(schema_arguments_rowspan), "display": display, "phraseologic": schema.phraseologic})
            
            ranks = FrameRankings.objects.filter(frame=schema)
            for rank in ranks:
                if rank.lexical_unit.id not in schema_unit_rank:
                    schema_unit_rank[rank.lexical_unit.id] = {}
                schema_unit_rank[rank.lexical_unit.id]["schema_" + str(schema.id) + "_"] = str(rank.rank)
        
        schemas_display.append(schema_display)
    
    
    context = {
               'lemma': lemma, 
               'schemas_display': schemas_display,
               'ranks': schema_unit_rank
              }

    return context
  
@render('examples.json')   
@ajax(method='get', encode_result=False)
def ajax_examples(request, lemma_id):

    lemma = Lemma.objects.get(id=lemma_id, old=False)
    nkjp_examples = lemma.nkjp_examples.all()
    lexical_units = LexicalUnit.objects.filter(Q(base = lemma.entry)|Q(base = lemma.entry + u' się'))
    
    lexical_units_ids = [lu.id for lu in lexical_units]
    
    examples_linked = []
    
    for nkjp_example in nkjp_examples:
        
        linked = LexicalUnitExamples.objects.filter(example=nkjp_example)
        meanings = []
        
        for link in linked:
            if link.lexical_unit.id in lexical_units_ids:
                meanings.append(link.lexical_unit.id)
        
        all_argselection = nkjp_example.arguments.all()
        arguments = []
        for argselection in all_argselection:
            pos = argselection.position.id
            args = argselection.arguments.all()
            for arg in args:
                arguments.append({'id': 'schema_' + str(nkjp_example.frame.id) + '_pos_' + str(pos) + '_arg_' + str(arg.id) + '_'})
        
        examples_linked.append({'schema': 'schema_' + str(nkjp_example.frame.id) + '_', 'arguments': arguments, 'id': nkjp_example.id, 'sentence': nkjp_example.sentence, 'source': nkjp_example.source.source, 'opinion': nkjp_example.opinion.opinion, 'meanings': meanings})
    
    
    context = {
                'examples': examples_linked
              }

    return context

  
@ajax(method='get', encode_result=False)  
def ajax_create_frame(request, lexical_units):
    #lexical_unit_id_list = request.GET.get('lexical_units', '')
    lexical_unit_id_list = lexical_units
    lexical_units_ids = [int(id) for id in lexical_unit_id_list.split(',')]
    lexical_units = []
    for lexical_unit_id in lexical_units_ids:
        lexical_units.append(LexicalUnit.objects.get(id=lexical_unit_id))
    unit_bases = [lexical_unit.base for lexical_unit in lexical_units]
    base = unit_bases[0].split(' ')[0]
    for unit_base in unit_bases:
        if unit_base != base and unit_base != base + u' się':
             raise SuspiciousOperation
    frame = SemanticFrame()
    frame.save()
    for lexical_unit in lexical_units:
        frame.lexical_units.add(lexical_unit)
    lemma = Lemma.objects.get(entry=lexical_unit.base, old=False)
    return ajax_frames(request, lemma.id)

@ajax(method='get', encode_result=False)
def ajax_create_complement(request, lemma_id, frame, roles):
    #lemma_id = int(request.GET.get('lemma_id', ''))
    lemma = Lemma.objects.get(id=lemma_id, old=False)
    frame_id = frame
    #frame_id = int(request.GET.get('frame', ''))
    frame = SemanticFrame.objects.get(id=frame_id)
    #role_id_list = request.GET.get('roles', '')
    role_id_list = roles
    role_ids = [int(id) for id in role_id_list.split(',')]
    roles = []
    for role_id in role_ids:
        roles.append(SemanticRole.objects.get(id=role_id))
    complement = Complement(frame=frame)
    complement.save()
    for role in roles:
        complement.roles.add(role)
    return ajax_frames(request, lemma.id)
    
@ajax(method='get', encode_result=False)
def ajax_update_meanings(request, operations, lemma_id):
    update_meanings(operations)
    return ajax_units(request)
    
@ajax(method='get', encode_result=False)
def ajax_modify_frames(request, operations, lemma_id):
    if not request.user.is_authenticated():
        return 'user logged out'
    modify_frames(lemma_id, operations, request.user)
    return ajax_frames(request)
        
@ajax(method='get', encode_result=True)         
def ajax_plWN_context_lookup(request, term):
    results = []
    term = unicode(term)
    if len(term) > 0:
        obj_results = LexicalUnit.objects.filter(base__startswith=term)
        results = get_ordered_lexical_units_bases(obj_results)
    return {'result': results}

def get_ordered_lexical_units_bases(lexical_units_query):
    last_unit_base = ''
    lexical_unit_bases = []
    ordered_lexical_units = lexical_units_query.order_by('base')
    for lexical_unit in ordered_lexical_units:
        if lexical_unit.base != last_unit_base:
            lexical_unit_bases.append(lexical_unit.base)
        last_unit_base = lexical_unit.base
    return lexical_unit_bases

@ajax(method='get')
def validate_semantics(request, lemma_id, new_status_id):
    error_msg = ''
    if Lemma.objects.get(id=lemma_id).old:
        error_msg = u'Odśwież hasło, widoczna wersja nie jest aktualna.'
    else:
        try:
            status = Lemma_Status.objects.get(id=new_status_id)
        except Lemma_Status.DoesNotExist:
            status = None
        if status and status.check_semantics:
            error_msg = validate_frames(lemma_id)
            if not error_msg:
                error_msg = validate_lexical_units(lemma_id)
            if not error_msg:
                error_msg = validate_schemas(lemma_id)
    return {'error_message': error_msg}