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

from django.db.models import Min

from dictionary.models import Argument, Frame, Lemma, NKJP_Example, Position
from semantics.change_log import store_old_versions
from semantics.models import Complement, GeneralSelectivePreference, FramePosition,\
                             LexicalUnitExamples, RelationalSelectivePreference, \
                             SelectivePreference, SelectivePreferenceRelations, \
                             SemanticFrame, SemanticRole, FrameOpinion                          
from wordnet.models import Hypernymy, LexicalUnit, Synonymy, Synset

def modify_frames(lemma_id, operations, user):
    store_old_versions(lemma_id, operations, user)
    make_operations(lemma_id, operations)

def make_operations(lemma_id, operations):
    translation = {'unit_id': {}, 'frame_id': {}, 'complement_id': {}, 'preference_id': {}}
    entry = Lemma.objects.get(id=lemma_id).entry_obj
    for operation in operations:
        if operation['operation'] == "create_frame":
            luids = [int(m['id']) for m in operation['meanings']]
            translation['frame_id'][int(operation['id'])] = create_frame(entry, luids)
        elif operation['operation'] == "add_unit":
            translation['unit_id'][int(operation['unit']['id'])] = add_unit(entry, operation['unit'])
        elif operation['operation'] == "remove_frame":
            if int(operation['id']) in translation['frame_id']:
                frame_id = translation['frame_id'][int(operation['id'])]
            else:
                frame_id = int(operation['id'])
            remove_frame(frame_id)
        elif operation['operation'] == "add_argument": 
            if int(operation['frame_id']) in translation['frame_id']:
                frame_id = translation['frame_id'][int(operation['frame_id'])]
            else:
                frame_id = int(operation['frame_id'])
            roles = [int(r) for r in operation['role']]
            translation['complement_id'][int(operation['id'])] = add_argument(frame_id, roles)
        elif operation['operation'] == "remove_argument":
            if int(operation['frame_id']) in translation['frame_id']:
                frame_id = translation['frame_id'][int(operation['frame_id'])]
            else:
                frame_id = int(operation['frame_id'])
            if int(operation['complement_id']) in translation['complement_id']:
                complement_id = translation['complement_id'][int(operation['complement_id'])]
            else:
                complement_id = int(operation['complement_id'])
            remove_argument(frame_id, complement_id)
        elif operation['operation'] == "connect":
            frame_data = operation['arg'].split('_')
            if int(frame_data[1]) in translation['frame_id']:
                frame_id = translation['frame_id'][int(frame_data[1])]
            else:
                frame_id = int(frame_data[1])
            if int(frame_data[3]) in translation['complement_id']:
                complement_id = translation['complement_id'][int(frame_data[3])]
            else:
                complement_id = int(frame_data[3])
            schema_data = operation['connect'].split('_')
            schema_id = int(schema_data[1])
            position_id = int(schema_data[3])
            argument_id = int(schema_data[5])
            alternation = int(schema_data[7])
            connect(frame_id, complement_id, schema_id, position_id, argument_id, alternation)
        elif operation['operation'] == "disconnect":
            frame_data = operation['arg'].split('_')
            if int(frame_data[1]) in translation['frame_id']:
                frame_id = translation['frame_id'][int(frame_data[1])]
            else:
                frame_id = int(frame_data[1])
            if int(frame_data[3]) in translation['complement_id']:
                complement_id = translation['complement_id'][int(frame_data[3])]
            else:
                complement_id = int(frame_data[3])
            schema_data = operation['connect'].split('_')
            schema_id = int(schema_data[1])
            position_id = int(schema_data[3])
            argument_id = int(schema_data[5])
            alternation = int(schema_data[7])
            disconnect(frame_id, complement_id, schema_id, position_id, argument_id, alternation)
        elif operation['operation'] == "assign_role":
            if int(operation['frame_id']) in translation['frame_id']:
                frame_id = translation['frame_id'][int(operation['frame_id'])]
            else:
                frame_id = int(operation['frame_id'])
            if int(operation['complement_id']) in translation['complement_id']:
                complement_id = translation['complement_id'][int(operation['complement_id'])]
            else:
                complement_id = int(operation['complement_id'])
            roles = [int(r) for r in operation['role']]
            assign_role(frame_id, complement_id, roles)
        elif operation['operation'] == "change_units":
            if int(operation['frame_id']) in translation['frame_id']:
                frame_id = translation['frame_id'][int(operation['frame_id'])]
            else:
                frame_id = int(operation['frame_id'])
            luids = [translation['unit_id'][int(m)] if int(m) in translation['unit_id'] else int(m) for m in operation['units']]
            change_units(frame_id, luids)
        elif operation['operation'] == "set_opinion":
            if int(operation['frame_id']) in translation['frame_id']:
                frame_id = translation['frame_id'][int(operation['frame_id'])]
            else:
                frame_id = int(operation['frame_id'])
            opinion = operation['opinion']
            set_opinion(frame_id, opinion)        
        elif operation['operation'] == "add_preference":
            # {operation: 'add_preference', frame_id: frame_id, complement_id: complement_id, preference_id: preference_id, preference: preference}
            if int(operation['frame_id']) in translation['frame_id']:
                frame_id = translation['frame_id'][int(operation['frame_id'])]
            else:
                frame_id = int(operation['frame_id'])
            if int(operation['complement_id']) in translation['complement_id']:
                complement_id = translation['complement_id'][int(operation['complement_id'])]
            else:
                complement_id = int(operation['complement_id'])
            preference_id = add_preference(frame_id, complement_id, operation['preference']['type'], operation['preference']['content'])
            translation['preference_id'][operation['preference_id']] = preference_id
        elif operation['operation'] == "remove_preference":
            # {operation: 'remove_preference', frame_id: frame_id, complement_id: complement_id, preference_id: preference_id}
            if int(operation['frame_id']) in translation['frame_id']:
                frame_id = translation['frame_id'][int(operation['frame_id'])]
            else:
                frame_id = int(operation['frame_id'])
            if int(operation['complement_id']) in translation['complement_id']:
                complement_id = translation['complement_id'][int(operation['complement_id'])]
            else:
                complement_id = int(operation['complement_id'])
            if operation['preference_id'] in translation['preference_id']:
                preference_id = translation['preference_id'][operation['preference_id']]
            else:
                preference_id = (operation['preference_id'][0], int(operation['preference_id'][1:]))
                
            remove_preference(frame_id, complement_id, preference_id)
        else:
            pass
        
def create_frame(entry, luids):
    frame = SemanticFrame(entry=entry)
    frame.save()
    for id in luids:
        lu = LexicalUnit.objects.get(id=id)
        frame.lexical_units.add(lu)
    return frame.id

def add_argument(frame_id, roles):
    if validate_roles(roles):
        frame = SemanticFrame.objects.get(id=frame_id)
        complement = Complement(frame=frame)
        complement.save()
        frame.complements.add(complement)
        role_objects = []
        for r in roles:
            role_objects.append(SemanticRole.objects.get(id=r))
        complement.roles = role_objects
        return complement.id
    
def remove_frame(frame_id):
    frame = SemanticFrame.objects.get(id=frame_id)
    frame.removed = True
    frame.save()
    
def remove_argument(frame_id, complement_id):
    Complement.objects.get(id=complement_id).delete()
    
def connect(frame_id, complement_id, schema_id, position_id, argument_id, alternation):
    schema = Frame.objects.get(id=schema_id)
    position = Position.objects.get(id=position_id)
    argument = Argument.objects.get(id=argument_id)
    fpas = FramePosition.objects.filter(frame=schema, position=position, argument=argument, alternation=alternation)
    if len(fpas) > 0:
        fpa = fpas[0]
    else:
        fpa = FramePosition(frame=schema, position=position, argument=argument, alternation=alternation)
        fpa.save()
    complement = Complement.objects.get(id=complement_id)
    complement.realizations.add(fpa)
    
def disconnect(frame_id, complement_id, schema_id, position_id, argument_id, alternation):
    schema = Frame.objects.get(id=schema_id)
    position = Position.objects.get(id=position_id)
    argument = Argument.objects.get(id=argument_id)
    fpas = FramePosition.objects.filter(frame=schema, position=position, argument=argument, alternation=alternation)
    if len(fpas) > 0:
        fpa = fpas[0]
    else:
        return
    complement = Complement.objects.get(id=complement_id)
    complement.realizations.remove(fpa)
    
def assign_role(frame_id, complement_id, roles):
    if validate_roles(roles):
        role_objects = []
        for r in roles:
            role_objects.append(SemanticRole.objects.get(id=r))
        complement = Complement.objects.get(id=complement_id)
        complement.roles = role_objects
        
def validate_roles(roles):
    role_objects = []
    for r in roles:
        role_objects.append(SemanticRole.objects.get(id=r))
    if len(role_objects) > 2:
        return False
    ok = False
    for r in role_objects:
        if not r.color == None:
            ok = not ok
    return ok

def change_units(frame_id, luids):
    frame = SemanticFrame.objects.get(id=frame_id)
    frame.lexical_units = []
    for id in luids:
        lu = LexicalUnit.objects.get(id=id)
        frame.lexical_units.add(lu)
        
def set_opinion(frame_id, opinion):
    frame = SemanticFrame.objects.get(id=frame_id)
    frame_opinion = FrameOpinion.objects.get(short=opinion)
    frame.opinion = frame_opinion
    frame.save()
        
# preference_id = add_preference(frame_id, complement_id, operation['preference']['type'], operation['preference']['content'])
def add_preference(frame_id, complement_id, preference_type, preference_content):

    complement = Complement.objects.get(id=complement_id)
    if complement.selective_preference is None:
        sp = SelectivePreference()
        sp.save()
        complement.selective_preference = sp
        complement.save()
    
    if preference_type == 'g':    
        general = GeneralSelectivePreference.objects.get(id=int(preference_content))
        complement.selective_preference.generals.add(general)
        return ('g', general.id)
    elif preference_type == 's':
        synset = Synset.objects.get(id=int(preference_content))
        complement.selective_preference.synsets.add(synset)
        return ('s', synset.id)
    elif preference_type == 'r':
        relation = SelectivePreferenceRelations.objects.get(id=int(preference_content['relation']))
        argument = [int(a) for a in preference_content['to'].split(',')]
        frame = SemanticFrame.objects.get(id=frame_id)
        candidates = Complement.objects.filter(frame=frame)
        found = None
        for c in candidates:
            if len(c.roles.all()) == len(argument):
                roles = [r.id for r in c.roles.all()]
                ok = True
                for a in argument:
                    if a not in roles:
                        ok = False
                if ok:
                    found = c
                    break
        if found is not None:
            rsp = RelationalSelectivePreference(relation=relation, to=found)
            rsp.save()
            complement.selective_preference.relations.add(rsp)
            return ('r', rsp.id)
        else:
            return -1
    else:
        return -1
    
# remove_preference(frame_id, complement_id, preference)
def remove_preference(frame_id, complement_id, preference):
    preference_type, preference_id = preference
    if preference_type == 'g':
        complement = Complement.objects.get(id=complement_id)
        g = complement.selective_preference.generals.get(id = int(preference_id))
        complement.selective_preference.generals.remove(g)
    elif preference_type == 's':
        complement = Complement.objects.get(id=complement_id)
        s = complement.selective_preference.synsets.get(id = int(preference_id))
        complement.selective_preference.synsets.remove(s)
    elif preference_type == 'r':
        complement = Complement.objects.get(id=complement_id)
        r = complement.selective_preference.relations.get(id = int(preference_id))
        complement.selective_preference.relations.remove(r)
        
        
def update_meanings(lemma_id, operations):
    translation = {}
    entry = Lemma.objects.get(id=lemma_id).entry_obj
    for operation in operations:
        if operation['operation'] == "set_glossa":
            if int(operation['unit']) in translation:
                unit = translation[int(operation['unit'])]
            else:
                unit = int(operation['unit'])
            set_glossa(unit, operation['value'])
        elif operation['operation'] == "add_example":
            if int(operation['unit']) in translation:
                unit = translation[int(operation['unit'])]
            else:
                unit = int(operation['unit'])
            add_example(unit, operation['example'])
        elif operation['operation'] == "remove_example": 
            if int(operation['unit']) in translation:
                unit = translation[int(operation['unit'])]
            else:
                unit = int(operation['unit'])
            remove_example(unit, operation['example'])
        elif operation['operation'] == "add_unit":
            translation[operation['unit']['id']] = add_unit(entry, operation['unit'])
        elif operation['operation'] == "remove_unit":
            luid = int(operation['luid'])
            if luid in translation:
                remove_unit(translation[luid])
            else:
                remove_unit(luid)
        else:
            pass
        
def set_glossa(unit_id, new_glossa):
    unit = LexicalUnit.objects.get(id=unit_id)
    unit.glossa = new_glossa
    unit.save()
    
def add_example(unit_id, example_id):
    unit = LexicalUnit.objects.get(id=unit_id)
    nkjp_example = NKJP_Example.objects.get(id=example_id)
    lue = LexicalUnitExamples(example=nkjp_example, lexical_unit=unit)
    lue.save()
    
def remove_example(unit_id, example_id):
    unit = LexicalUnit.objects.get(id=unit_id)
    nkjp_example = NKJP_Example.objects.get(id=example_id)
    lue = LexicalUnitExamples.objects.get(example=nkjp_example, lexical_unit=unit)
    lue.delete()
    
def add_unit(entry, unit): # returns new id
    
    s1 = Synset(id=(min(Synset.objects.all().aggregate(Min('id'))['id__min'], 0) - 1))
    s1.save()
    lu = LexicalUnit(entry=entry, base=unit['base'], sense=unit['sense'], pos=unit['pos'], glossa=unit['glossa'], luid=-1, synset=s1)
    lu.save()
    
    
    if int(unit['relation']) == 1:
        s2 = Synset.objects.get(id=int(unit['to']))
        r = Synonymy(parent=s1, child=s2)
        r.save()
        r = Synonymy(parent=s2, child=s1)
        r.save()
    elif int(unit['relation']) == 0:
        s2 = Synset.objects.get(id=int(unit['to']))
        r = Hypernymy(parent=s1, child=s2)
        r.save()
    else:
        pass
    
    return lu.id

def remove_unit(luid):
    lu = LexicalUnit.objects.get(id=luid)
    if lu.luid is not None and lu.luid >= 0:
        return
    else:
        lu.delete()
