import json
import requests
import simplejson
from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import JsonResponse, HttpResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt

from common.decorators import ajax_required, ajax
from entries.polish_strings import EXAMPLE_SOURCE, EXAMPLE_OPINION
from entries.views import get_scroller_params, get_alternations, get_prefs_list, schema2dict, \
    frame2dict, collect_forms, get_filtered_objects, get_local_schema_filter_form, get_local_frame_filter_form
from financial_settlement.models import FinStatement
from importer.unification.UnificationPreprocessXML import UnificationPreprocessHandler
from meanings.models import LexicalUnit
from semantics.choices import FrameStatus
from semantics.models import Frame, ArgumentRole, SemanticRole, RoleAttribute, RoleType
from syntax.models import Schema
from unifier.models import UnifiedFrameArgument, UnifiedRelationalSelectionalPreference, UnifiedFrame, \
    UnifiedFrame2SlowalFrameMapping, UnifiedFrameArgumentSlowalFrameMapping, HierarchyModel
from users.models import Assignment
from . import choices
from xml.etree.ElementTree import Element, SubElement, tostring
import io
from xml.sax import handler, make_parser
from django.db.models import Q


@ajax_required
@transaction.atomic
def save_synset_preference(request):
    if request.method == 'POST':
        frame_id = request.POST['frame_id']
        complement_id = request.POST['complement_id']
        synset_preference_id = request.POST['synset_preference_id']

        unifiedFrameArgument = UnifiedFrameArgument.objects.get(unified_frame_id=int(frame_id), id=int(complement_id))
        unifiedFrameArgument.synsets.add(int(synset_preference_id))
        unifiedFrameArgument.save()

        updateArgumentPreferencesCount(unifiedFrameArgument)

    return JsonResponse({})


@ajax_required
@transaction.atomic
def save_predefined_preference(request):
    if request.method == 'POST':
        frame_id = request.POST['frame_id']
        complement_id = request.POST['complement_id']
        predefined_preference_id = request.POST['predefined_preference_id']

        unifiedFrameArgument = UnifiedFrameArgument.objects.get(unified_frame_id=int(frame_id), id=int(complement_id))
        unifiedFrameArgument.predefined.add(int(predefined_preference_id))
        unifiedFrameArgument.save()

        updateArgumentPreferencesCount(unifiedFrameArgument)

    return JsonResponse({})


@ajax_required
@transaction.atomic
def save_relational_selectional_preference(request):
    if request.method == 'POST':
        frame_id = request.POST['frame_id']
        complement_id_from = request.POST['complement_id_from']
        complement_id_to = request.POST['complement_id_to']
        relation_id = request.POST['relation_id']

        unifiedFrameArgument = UnifiedFrameArgument.objects.get(unified_frame_id=int(frame_id),
                                                                id=int(complement_id_from))
        relationalSelectionalPreference = UnifiedRelationalSelectionalPreference(to_id=complement_id_to,
                                                                                 relation_id=relation_id)
        relationalSelectionalPreference.save()
        unifiedFrameArgument.relations.add(relationalSelectionalPreference)
        unifiedFrameArgument.save()

        updateArgumentPreferencesCount(unifiedFrameArgument)

    return JsonResponse({})


@ajax_required
def get_unified_frames(request):
    if request.method == 'POST':
        scroller_params = get_scroller_params(request.POST)
        exclude_status = request.GET.get('exclude_status')
        restrict_to_user = request.GET.get('restrict_to_user')

        errors_dict = dict()

        if 'unified_frame_form' in request.session:
            forms = collect_forms(request.session['unified_frame_form'], errors_dict)
            unified_frames = get_filtered_objects(forms).filter()
        else:
            unified_frames = UnifiedFrame.objects.all()

        if scroller_params['filter']:
            unified_frames = unified_frames.filter(title__startswith=scroller_params['filter'])

        # TODO: zapytać, czy mamy zaciągać powiązane ramy zunifikowane poprzez Enrty (slowal_frame -> lexical_units -> entries -> related_entries)
        # linked_ids = set()
        # if request.session['show_linked_entries']:
        #     entries_linked = Entry.objects.filter(pk__in=(
        #         Entry.objects
        #             .filter(subentries__schema_hooks__argument_connections__schema_connections__subentry__entry__in=entries)
        #             .exclude(id__in=entries)
        #     )).distinct()
        #     entries = entries | entries_linked
        #     linked_ids = set(entries_linked.values_list('id', flat=True))

        res = {}

        for unifiedFrame in unified_frames:
            res[unifiedFrame.id] = {
                'id': str(unifiedFrame.id),
                'title': unifiedFrame.title,
                'status': unifiedFrame.status,
                'assignee_username': assignment.user.username if (
                    assignment := unifiedFrame.assignments.first()) else None,
                'hierarchy_exists': unifiedFrame.hasHierarchyElems,
            }

        resProcessed = []
        for key, value in res.items():
            if (exclude_status is None or value['status'] != exclude_status) and \
                    (restrict_to_user is None or value['assignee_username'] == restrict_to_user):
                resProcessed.append(value)

        ret = {
            'draw': scroller_params['draw'],
            'recordsTotal': len(resProcessed),
            'recordsFiltered': len(resProcessed),
            'data': resProcessed
        }

        return JsonResponse(ret)
    return JsonResponse({})


def unifiedFrame2dict(frame):
    return {
        'id': frame.id,
        'title': frame.title,
        'status': str(frame.status),
        'assignee_username': assignment.user.username if (assignment := frame.assignments.first()) else None,
        'arguments': [
            {
                'str': str(a),
                'id': a.id,
                'role': {
                    'str': '{}{}'.format(a.role.role.role.lower(),
                                         ' ' + a.role.attribute.attribute.lower() if a.role.attribute else
                                         '') if a.role is not None else None,
                    'id': str(a.role.id)
                } if a.role is not None else None,
                'role_type': a.role_type.type.lower() if a.role_type is not None else '',
                'preferences': get_prefs_list(a),
                'proposed_roles': [{
                    'str': '{}{}'.format(r.role.role.lower(),
                                         ' ' + r.attribute.attribute.lower() if r.attribute else ''),
                    'id': str(r.id)
                } for r in a.proposed_roles.all()],
            } for a in sorted(frame.unified_arguments.all(), key=lambda x: x.id)
        ],

        'slowal_frame_mapping': [
            {
                'slowal_frame_id': slowal_frame_mapping.slowal_frame.id,
                'slowal_frame_argument_mapping': [
                    {
                        'unified_frame_agrument_id': slowal_frame_arg_mapping.unified_agrument.id,
                        'slowal_frame_agrument_id': slowal_frame_arg_mapping.slowal_agrument.id,
                    } for slowal_frame_arg_mapping in slowal_frame_mapping.unified_frame_argument_mapping.all()
                ]
            } for slowal_frame_mapping in frame.unified_frame_2_slowal_frame.all()
        ]

    }


def get_examples(frames):
    examples = []
    for frame in frames:
        for argument in frame.arguments.all():
            for connection in argument.example_connections.all():
                example = connection.example
                frame_ids, argument_ids, lu_ids = set(), set(), set()
                schema_ids, phrases, phrases_syntax = set(), set(), set()
                positions = set()
                for connection in example.example_connections.all():
                    for argument in connection.arguments.all():
                        frame_ids.add(argument.frame.id)
                        argument_ids.add('{}-{}'.format(argument.frame.id, argument.id))
                    if connection.lexical_unit:
                        lu_ids.add(connection.lexical_unit.id)
                    for hook in connection.schema_connections.all():
                        schema_ids.add(hook.schema.id)
                        phrases.add('{}-{}-{}-{}'.format(hook.schema.id, hook.position.id, hook.phrase_type.id,
                                                         hook.alternation - 1))
                        phrases_syntax.add('{}-{}-{}'.format(hook.schema.id, hook.position.id, hook.phrase_type.id))
                        positions.add('{}-{}'.format(hook.schema.id, hook.position.id))
                elem = {
                    'id': str(example.id),
                    'sentence': example.sentence,
                    'source': EXAMPLE_SOURCE()[example.source.key],
                    'opinion': EXAMPLE_OPINION()[example.opinion.key],
                    'note': example.note,
                    'frame_ids': sorted(frame_ids),
                    'argument_ids': sorted(argument_ids),
                    'lu_ids': sorted(lu_ids),
                    'schema_ids': sorted(schema_ids),
                    'phrases': sorted(phrases),
                    'phrases_syntax': sorted(phrases_syntax),
                    'positions': sorted(positions),
                }
                if elem not in examples:
                    examples.append(elem)
    return sorted(examples, key=lambda x: x['sentence'])


def get_unified_frame_json(unified_frame, request):
    local_schema_filter_form = None
    local_frame_filter_form = None
    if 'no_filters' in request.POST:
        apply_filters = not simplejson.loads(request.POST['no_filters'])
        local_schema_filter_form = get_local_schema_filter_form(apply_filters, request)
        local_frame_filter_form = get_local_frame_filter_form(apply_filters, request)

    slowal_frames_db = Frame.objects.filter(
        id__in=unified_frame.unified_frame_2_slowal_frame.values("slowal_frame_id")).distinct()

    if local_frame_filter_form:
        slowal_frames_db = get_filtered_objects(local_frame_filter_form, slowal_frames_db)

    slowal_frames = slowal_frames_db.all()

    all_schema_objects = Schema.objects.filter(
        schema_hooks__argument_connections__argument__frame__in=slowal_frames).distinct()

    if local_schema_filter_form:
        all_schema_objects = get_filtered_objects(local_schema_filter_form, all_schema_objects)

    slowal_frames_dict = []
    for slowal_frame in slowal_frames:
        dict = frame2dict(slowal_frame, slowal_frame.lexical_units.all())
        slowal_frames_dict.append(dict)

    alternations, realisation_phrases, realisation_descriptions = get_alternations(all_schema_objects, slowal_frames)
    examples = get_examples(slowal_frames)

    all_schema_objects_dict = [schema2dict(schema, schema.subentries.all()[0].negativity, request.LANGUAGE_CODE) for
                               schema in all_schema_objects]

    subentries = [{
        'str': None,
        'schemata': all_schema_objects_dict
    }]

    unifiedFrame_dict = unifiedFrame2dict(unified_frame)

    return {'unified_frame_id': unified_frame.id, 'unified_frame': unifiedFrame_dict, 'subentries': subentries,
            'frames': slowal_frames_dict, 'alternations': alternations, 'realisation_phrases': realisation_phrases,
            'realisation_descriptions': realisation_descriptions, 'examples': examples,
            'last_visited': request.session['last_visited']}


@ajax_required
@login_required
def get_unified_frame(request):
    if request.method == 'POST':
        # TODO (*)
        # form = EntryForm(request.POST)
        unified_frame_id = request.POST['unified_frame_id']
        # TODO (*)
        if unified_frame_id.isdigit():  # and form.is_valid():
            unified_frame_id = int(unified_frame_id)
            request.session.modified = True
            unified_frame = UnifiedFrame.objects.get(id=unified_frame_id)
            return JsonResponse(get_unified_frame_json(unified_frame, request))

    return JsonResponse({})


@ajax_required
@transaction.atomic
def extract_frames_to_new_frame(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        target_unified_frame_id = request.POST['target_unified_frame_id']
        slowal_frame_ids = json.loads(request.POST['slowal_frame_ids'])

        unified_frame = UnifiedFrame.objects.get(id=unified_frame_id)
        slowal_frames = Frame.objects.filter(id__in=slowal_frame_ids)
        new_unified_frame = None
        if target_unified_frame_id != '':
            new_unified_frame = UnifiedFrame.objects.get(id=target_unified_frame_id)
        new_frame_fullfiled_and_saved = unified_frame.extract_frames_to(slowal_frames=slowal_frames,
                                                                        new_frame=new_unified_frame)
        return JsonResponse(get_unified_frame_json(new_frame_fullfiled_and_saved, request))
    return JsonResponse({})


@ajax_required
@transaction.atomic
def remove_selectional_preference(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        argument_id = request.POST['argument_id']
        preference_ids = json.loads(request.POST['preference_ids'])

        unified_frame_argument = UnifiedFrameArgument.objects.get(id=argument_id, unified_frame=unified_frame_id)
        unified_frame_argument.predefined.set(unified_frame_argument.predefined.exclude(id__in=preference_ids))
        unified_frame_argument.synsets.set(unified_frame_argument.synsets.exclude(id__in=preference_ids))
        unified_frame_argument.relations.set(unified_frame_argument.relations.exclude(id__in=preference_ids))
        unified_frame_argument.save()

        updateArgumentPreferencesCount(unified_frame_argument)

        return JsonResponse({})
    return JsonResponse({})


def updateArgumentPreferencesCount(unified_frame_argument):
    unified_frame_argument.preferences_count = unified_frame_argument.predefined.count() + \
                                               unified_frame_argument.synsets.count() + \
                                               unified_frame_argument.relations.count()
    unified_frame_argument.save()


@ajax_required
@transaction.atomic
def duplicate_unified_frame(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        target_unified_frame_title = request.POST['target_unified_frame_title']

        unified_frame = UnifiedFrame.objects.get(id=unified_frame_id)
        new_frame = unified_frame.duplicate(new_frame_title=target_unified_frame_title)
        return JsonResponse(get_unified_frame_json(new_frame, request))
    return JsonResponse({})


@ajax_required
@transaction.atomic
def change_slowal2unified_fram_argument_mapping(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        slowal_frame_id = request.POST['slowal_frame_id']
        slowal_frame_selected_arguments = json.loads(request.POST['slowal_frame_selected_arguments'])

        unifiedFrame2SlowalFrameMapping = UnifiedFrame2SlowalFrameMapping.objects.get(unified_frame_id=unified_frame_id,
                                                                                      slowal_frame=slowal_frame_id)

        arg1_id = int(slowal_frame_selected_arguments[0])
        arg2_id = int(slowal_frame_selected_arguments[1])

        unifiedFrameArgumentMapping1 = UnifiedFrameArgumentSlowalFrameMapping.objects.get(
            unified_frame_mapping=unifiedFrame2SlowalFrameMapping,
            slowal_agrument_id=arg1_id) if arg1_id >= 0 else None
        unifiedFrameArgumentMapping2 = UnifiedFrameArgumentSlowalFrameMapping.objects.get(
            unified_frame_mapping=unifiedFrame2SlowalFrameMapping,
            slowal_agrument_id=arg2_id) if arg2_id >= 0 else None

        if unifiedFrameArgumentMapping1 is not None and unifiedFrameArgumentMapping2 is not None:
            # mamy oba argumenty, zamieniamy miescami
            unifiedFrameArgumentMapping1.slowal_agrument_id = arg2_id
            unifiedFrameArgumentMapping2.slowal_agrument_id = arg1_id
            unifiedFrameArgumentMapping1.save()
            unifiedFrameArgumentMapping2.save()
        elif unifiedFrameArgumentMapping1 is not None and unifiedFrameArgumentMapping2 is None:
            # mamy lewy argument, prawy jest Empty
            unifiedFrameArgumentMapping1.unified_agrument_id = -arg2_id
            unifiedFrameArgumentMapping1.save()
        elif unifiedFrameArgumentMapping1 is None and unifiedFrameArgumentMapping2 is not None:
            # mamy prawy argument, lewy jest Empty
            unifiedFrameArgumentMapping2.unified_agrument_id = -arg1_id
            unifiedFrameArgumentMapping2.save()

        return JsonResponse({})
    return JsonResponse({})


@ajax_required
@transaction.atomic
def change_slowal_frame_status(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        slowal_frame_id = request.POST['slowal_frame_id']
        status = request.POST['status']

        frame = Frame.objects.get(pk=slowal_frame_id)
        frame.status = status
        frame.save()
        return JsonResponse({})
    return JsonResponse({})


@ajax_required
@transaction.atomic
def save_unified_frame_title(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        unified_frame_title = request.POST['unified_frame_title']

        unifiedFrame = UnifiedFrame.objects.get(id=unified_frame_id)

        if unifiedFrame:
            unifiedFrame.title = unified_frame_title
            unifiedFrame.save()
    return JsonResponse({})


@ajax_required
@transaction.atomic
def save_selected_role(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        complement_id = request.POST['complement_id']
        role_id = request.POST['role_id']

        unifiedFrameArgument = UnifiedFrameArgument.objects.get(unified_frame_id=int(unified_frame_id),
                                                                id=int(complement_id))
        unifiedFrameArgument.role_id = role_id
        unifiedFrameArgument.save()
    return JsonResponse({})


@ajax_required
@transaction.atomic
def save_new_role(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        complement_id = request.POST['complement_id']
        role_id = request.POST['role_id']
        role_type = request.POST['role_type']
        attribute_id = request.POST.get('attribute_id', None)

        argumentRole = ArgumentRole.objects.filter(role_id=role_id, attribute_id=attribute_id).first()
        if argumentRole is None:
            argumentRole = ArgumentRole(role=SemanticRole.objects.get(pk=role_id),
                                        attribute=RoleAttribute.objects.get(pk=attribute_id))
            argumentRole.save()

        unifiedFrameArgument = UnifiedFrameArgument.objects.get(unified_frame_id=unified_frame_id, id=complement_id)
        unifiedFrameArgument.role = argumentRole
        unifiedFrameArgument.role_type = RoleType.objects.get(type=role_type)
        unifiedFrameArgument.save()
    return JsonResponse({})


@ajax_required
@transaction.atomic
def add_argument(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']

        unifiedFrame = UnifiedFrame.objects.get(pk=unified_frame_id)
        newUnifiedFrameArgument = UnifiedFrameArgument.objects.create(unified_frame=unifiedFrame)
        newUnifiedFrameArgument.save()
        unifiedFrame.arguments_count = unifiedFrame.arguments_count + 1
        unifiedFrame.save()
    return JsonResponse({})


@ajax_required
@transaction.atomic
def remove_argument(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        complement_id = request.POST['complement_id']

        newUnifiedFrameArgument = UnifiedFrameArgument.objects.get(id=complement_id)
        newUnifiedFrameArgument.delete()
        unifiedFrame = UnifiedFrame.objects.get(pk=unified_frame_id)
        unifiedFrame.arguments_count = unifiedFrame.arguments_count - 1
        unifiedFrame.save()
    return JsonResponse({})


@ajax_required
@transaction.atomic
def change_unified_frame_status_to_ready(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        unifiedFrame = UnifiedFrame.objects.get(pk=unified_frame_id)
        unifiedFrame.status = choices.UnifiedFrameStatus.READY

        for mapping in unifiedFrame.unified_frame_2_slowal_frame.all():
            mapping.verified = True
            mapping.save()

        unifiedFrame.save()
    return JsonResponse({})


@ajax_required
@transaction.atomic
def change_unified_frame_status_to_verified_by_superleksykograf(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        unifiedFrame = UnifiedFrame.objects.get(pk=unified_frame_id)
        unifiedFrame.status = choices.UnifiedFrameStatus.VERIFIED

        # save fin statement info for current active fin statement
        fin_statements = FinStatement.objects.filter(is_active=True)
        if fin_statements.count() == 1:
            active_fin_statement = fin_statements[0]
            unifiedFrame.fin_statement = active_fin_statement

        unifiedFrame.save()
    return JsonResponse({})


def create_unified_frame(lu_id):
    response = requests.post('http://127.0.0.1:8000/en/unifier/build_unified_frame_xml/?lu_id=' + str(lu_id))

    parser = make_parser()
    parser.setFeature(handler.feature_external_ges, False)

    unifiedFrameXMLHandler = UnificationPreprocessHandler()
    parser.setContentHandler(unifiedFrameXMLHandler)

    f = io.StringIO(response.content.decode("utf-8"))

    parser.parse(f)

    unified_frame_id = unifiedFrameXMLHandler.unified_frame_ids[0]

    return unified_frame_id


@csrf_exempt
def build_unified_frame_xml(request):
    if request.method == 'POST':
        lu_id = request.GET.get('lu_id')

        lu = LexicalUnit.objects.get(pk=lu_id)
        frames = list(lu.frames.all())
        if len(frames) > 0:
            matchingElem = Element('matching')
            unifier_frameElem = SubElement(matchingElem, 'unifier_frame')

            frame = frames[0]

            arguments = list(frame.arguments.all())
            argCnt = len(arguments)
            for id in range(argCnt):
                argumentElem = SubElement(unifier_frameElem, 'argument', attrib={'id': str(id)})
                semantic_role_typeElem = SubElement(argumentElem, 'semantic_role', attrib={'type': 'role'})
                rolesElem = SubElement(argumentElem, 'roles')
                roleElem1 = SubElement(rolesElem, 'role', attrib={'name': 'Initiator'})
                roleElem2 = SubElement(rolesElem, 'role', attrib={'name': 'Manner'})

            connectionsElem = SubElement(unifier_frameElem, 'connections')

            slowal_frameElem = SubElement(connectionsElem, 'slowal_frame', attrib={'id': str(frame.id)})

            arguments_connectionsElem = SubElement(slowal_frameElem, 'arguments_connections')

            for id, argument in enumerate(arguments):
                arguments_connectionElem = SubElement(arguments_connectionsElem, 'arguments_connection',
                                                      attrib={'unifier_argument_id': str(id),
                                                              'slowal_id': str(argument.id)})
            xml = tostring(matchingElem)
            return HttpResponse(xml)

    return HttpResponse()


@ajax_required
@transaction.atomic
def save_unified_frame_title(request):
    if request.method == 'POST':
        unified_frame_id = request.POST['unified_frame_id']
        unified_frame_title = request.POST['unified_frame_title']

        unifiedFrame = UnifiedFrame.objects.get(id=unified_frame_id)

        if unifiedFrame:
            unifiedFrame.title = unified_frame_title
            unifiedFrame.save()
    return JsonResponse({})


@ajax_required
@transaction.atomic
def frame_assign(request):
    if request.method == 'POST':

        lu_id = request.POST['lu_id']
        unified_frame_title = request.POST['unified_frame_title']

        unified_frame_pk = create_unified_frame(lu_id)

        unifiedFrame = get_object_or_404(
            UnifiedFrame.objects,
            pk=unified_frame_pk,
        )
        unifiedFrame.title = unified_frame_title
        unifiedFrame.save()

        Assignment.assign(user=request.user, subject=unifiedFrame)

        slowal_frames = [connection.slowal_frame for connection in unifiedFrame.unified_frame_2_slowal_frame.all()]

        for slowal_frame in slowal_frames:
            slowal_frame.status = FrameStatus.PROCESSING
            Assignment.assign(user=request.user, subject=slowal_frame)
            slowal_frame.save()

        ret = {
            'unified_frame_id': unified_frame_pk
        }

        return JsonResponse(ret)
    return JsonResponse({})


def removeUnifiedFrameMappingsAndAssigments(unified_frame_id):
    # odpianie z ramy zunifikowanej
    unifiedFrame2SlowalFrameMappings = UnifiedFrame2SlowalFrameMapping.objects.filter(unified_frame_id=unified_frame_id)
    for unifiedFrame2SlowalFrameMapping in unifiedFrame2SlowalFrameMappings:
        unifiedFrame2SlowalFrameMapping.slowal_frame.status = 'N'
        unifiedFrame2SlowalFrameMapping.slowal_frame.save()
        Assignment.delete(subject_id=unifiedFrame2SlowalFrameMapping.slowal_frame.id)
        unifiedFrameArgumentSlowalFrameMappings = UnifiedFrameArgumentSlowalFrameMapping.objects.filter(
            unified_frame_mapping=unifiedFrame2SlowalFrameMapping)
        unifiedFrameArgumentSlowalFrameMappings.delete()
        unifiedFrame2SlowalFrameMapping.delete()
    Assignment.delete(subject_id=unified_frame_id)


@ajax(login_required=True, method='post')
@transaction.atomic
def delete_unified_frame(request, unified_frame_id):
    removeUnifiedFrameMappingsAndAssigments(unified_frame_id)
    UnifiedFrameArgument.objects.filter(unified_frame_id=unified_frame_id).delete()
    UnifiedFrame.objects.get(id=unified_frame_id).delete()

    return {}


@ajax_required
@transaction.atomic
def hierarchy_assign(request):
    """
    Assign hierarchy relationship.
    The request has to contain 'hyponym_id' and 'hypernym_id' that represent
    unified frames taking part in the relationship.
    :param request: http request
    :return: Json response containg single field 'succ': (true|false) or 'exists': 'true'
    """
    if request.method == 'POST':

        hyponym_id = request.POST['hyponym_id']
        hyperonym_id = request.POST['hyperonym_id']

        curr1 = HierarchyModel.objects.filter(hyponym_id=hyponym_id, hyperonym_id=hyperonym_id)
        curr2 = HierarchyModel.objects.filter(hyponym_id=hyperonym_id, hyperonym_id=hyponym_id)

        if curr1 or curr2:
            return JsonResponse({'exists': 'true'})

        hierarchy = HierarchyModel()
        hierarchy.hyponym_id = hyponym_id
        hierarchy.hyperonym_id = hyperonym_id
        hierarchy.save()

        setHierarchyExistsInfo(hyponym_id)
        setHierarchyExistsInfo(hyperonym_id)

        return JsonResponse({'succ': 'true'})
    return JsonResponse({})


def setHierarchyExistsInfo(unified_fram_id):
    unified_frame = UnifiedFrame.objects.get(pk=unified_fram_id)
    unified_frame.hasHierarchyElems = True
    unified_frame.save()


@ajax_required
@transaction.atomic
def hierarchy_unassign(request):
    """
    Unassign hierarchy relationship.
    The request has to contain 'rel1_id' and 'rel2_id' that represent unified frame IDs hierarchy pair.
    :param request: http request
    :return: Json response containg single field 'succ' = (true|false)
    """
    if request.method == 'POST':

        rel1_id = request.POST['rel1_id']
        rel2_id = request.POST['rel2_id']

        curr1 = HierarchyModel.objects.filter(hyponym_id=rel1_id, hyperonym_id=rel2_id)

        succ = False

        if curr1:
            curr1.delete()
            succ = True
        else:
            curr2 = HierarchyModel.objects.filter(hyponym_id=rel2_id, hyperonym_id=rel1_id)
            if curr2:
                curr2.delete()
                succ = True

        # check if there is any heierarchy elements for unified frames from request
        setNoHierarchyExistsInfo(rel1_id)
        setNoHierarchyExistsInfo(rel2_id)

        return JsonResponse({'succ': succ})


def setNoHierarchyExistsInfo(unified_frame_id):
    """
    Set unified frame field 'hasHierarchyElems' as false if needed.
    :param unified_frame_id: Unified frame id
    """
    rel_1_hierarchy_count = HierarchyModel.objects.filter(Q(hyponym_id=unified_frame_id) |
                                                          Q(hyperonym_id=unified_frame_id)).count()
    if rel_1_hierarchy_count == 0:
        # set info of no hierarchy elems in unified frame -> used to color unified frame list component
        unified_frame = UnifiedFrame.objects.get(pk=unified_frame_id)
        unified_frame.hasHierarchyElems = False
        unified_frame.save()


@ajax_required
@transaction.atomic
def get_hierarchy_hyponyms(request, unified_frame_id):
    """
        Returning hierarchy hyponyms for a given unified_frame_id
    :param request: http request
    :param unified_frame_id: Unifird fram id for witch hyponyms will be returned
    :return: Json response containing list of unified frames as hyponyms
    """
    hyponyms = HierarchyModel.objects.filter(hyperonym_id=unified_frame_id)

    res = []
    for unifiedFrame in hyponyms:
        res.append(get_unified_frame_json(unifiedFrame.hyponym, request))
    res = {
        'hyponyms': res
    }

    return JsonResponse(res)


@ajax_required
@transaction.atomic
def get_hierarchy_hyperonyms(request, unified_frame_id):
    """
        Returning hierarchy hypernyms for a given unified_frame_id.
    :param request: http request
    :param unified_frame_id: Unifird fram id for witch hyperonyms will be returned
    :return: Json response containing list of unified frames as hyperonyms
    """
    hyperonyms = HierarchyModel.objects.filter(hyponym_id=unified_frame_id)

    res = []
    for unifiedFrame in hyperonyms:
        res.append(get_unified_frame_json(unifiedFrame.hyperonym, request))
    res = {
        'hyperonyms': res
    }

    return JsonResponse(res)