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

#Copyright (c) 2014, Bartłomiej Nitoń
#All rights reserved.

#Redistribution and use in source and binary forms, with or without modification, are permitted provided 
#that the following conditions are met:

#    Redistributions of source code must retain the above copyright notice, this list of conditions and 
#    the following disclaimer.
#    Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
#    and the following disclaimer in the documentation and/or other materials provided with the distribution.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
# POSSIBILITY OF SUCH DAMAGE.

from operator import itemgetter, or_

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

from accounts.models import can_modify_phraseology_only 
from common.decorators import render, ajax, AjaxError
from common.js_to_obj import jsArgToObj
from dictionary.forms import AddArgumentForm, ArgPropositionsForm, Atribute_Model, \
                             AtributeChoiceForm, AtributeTextForm, ValueAttrMultiValueForm, \
                             TextAttrMultiValueForm, PositionsForm, SelectArgumentForm, \
                             ArgumentsForm
from dictionary.models import Argument, Argument_Model, Atribute, Atribute_Value, AttributeSubparameter, \
                              AttributeParameterModel, Entry, Lemma, Position, AttributeType, \
                              AttrValueSelectionMode, \
                              sortatributes, sortPositions, sortArguments, get_or_create_attr_parameter, \
                              get_attr_models_to_exclude, is_morfeusz_exception, get_or_create_attribute, \
                              get_or_create_parameter_attr_value
from settings import MORFEUSZ2

@render('argument_form.html')
@ajax(method='post', encode_result=False)
def argument_form(request, form_data, lemma_id, position_arguments, 
                    selected_arg='', phraseologic=False, 
                    lex_arg_choosing=False, # flagi okreslajace w jakim polu wybierany jest argument
                    position_or_arguments_arg_choosing=False,
                    creating_editing_new_realization=False):
    form_dict = {}
    has_realizations = False
    form_type = 'standard'
    if position_or_arguments_arg_choosing:
        form_type = 'position_or_arguments_arg_choosing'
    elif lex_arg_choosing:
        form_type = 'lex_arg_choosing'
    if creating_editing_new_realization:
        form_type = 'realization_arg'
    phraseologic_modify_only = can_modify_phraseology_only(request.user, phraseologic)
    lemma_pos = None
    if lemma_id:
        lemma_obj = Lemma.objects.get(pk=lemma_id)
        lemma_pos = lemma_obj.entry_obj.pos
    # jesli wybieramy argument do pozycji w modyfikacji lub jeden 
    # z argumentow do zleksykalizowanego compara to dostepne sa modele argumentow
    # dla wszystkich czesci mowy
    if position_or_arguments_arg_choosing or lex_arg_choosing: 
        lemma_pos = None
    propositions_form = None
    attr_subforms_values = []
    
    if form_data:
        form_dict = dict((x['name'], x['value']) for x in form_data)
    
    # TODO tutaj dodano wykluczenie funkcjonalnosci "czesto koordynujace sie argumenty", poprawic na madrzejsze
    if position_arguments and lemma_obj.entry_obj.pos.tag == 'verb' and not phraseologic_modify_only:
        propositions_form = create_propositions_form(form_dict, position_arguments)
    arg_model = get_argument_model(form_dict, phraseologic_modify_only)
    if form_dict: # zaznaczono wartosci
    # prezentowanie formularza na podstawie reprezentacji teksowej argumentu
        if form_dict['arg_id']:
            argument = Argument.objects.get(id=form_dict['arg_id'])
            attr_subforms_values = argument_to_form_values(argument) 
        elif form_dict['arg_type']:
            attr_subforms_values = form_dict['subform_values']
        selected_arg_model = None
        if selected_arg:
            selected_arg_obj = Argument.objects.get(id=selected_arg)
            selected_arg_model = Argument_Model.objects.get(arg_model_name=selected_arg_obj.type)
        if selected_arg_model and phraseologic_modify_only and not selected_arg_model.phraseologic:
            attr_subforms_values = fill_argument_attr_field(attr_subforms_values, selected_arg)

    type_form, attr_sheets = create_argument_form(arg_model=arg_model, 
                                                  subforms_values=attr_subforms_values, 
                                                  pos=lemma_pos, 
                                                  phraseologic=phraseologic_modify_only,
                                                  form_type=form_type)   
    if arg_model:
        has_realizations = arg_model.has_realizations
    return {'propositions_form': propositions_form,
            'type_form': type_form,
            'attr_sheets': attr_sheets,
            'has_realizations': has_realizations}
    
def fill_argument_attr_field(subforms_values, selected_argument_id):
    subforms_values[0] = [selected_argument_id]
    return subforms_values
    
def argument_to_form_values(argument):
    form_values = []
    sorted_attributes = sortatributes(argument)
    for attribute in sorted_attributes:
        form_values.append(attribute_to_form_values(attribute))
    return form_values

def attribute_to_form_values(attribute):
    attribute_form_values = []
    attribute_model = Atribute_Model.objects.get(atr_model_name=attribute.type)
    attribute_value_type = attribute_model.type.sym_name
    selection_modes = attribute_model.values_selection_modes
    if attribute_value_type == 'text' and not selection_modes.exists():
        attribute_form_values = get_simple_text_attr_form_values(attribute)
    elif attribute_value_type == 'text' and selection_modes.exists():
        attribute_form_values = get_complex_text_attr_form_values(attribute) 
    elif attribute_value_type == 'parameter' and not selection_modes.exists():
        attribute_form_values = get_simple_parameter_attr_form_values(attribute)
    elif attribute_value_type == 'parameter' and selection_modes.exists():
        attribute_form_values = get_complex_parameter_attr_form_values(attribute)
    elif attribute_value_type == 'argument' and not selection_modes.exists():
        attribute_form_values = get_simple_argument_attr_form_values(attribute)
    elif attribute_value_type == 'argument' and selection_modes.exists():
        attribute_form_values = get_complex_argument_attr_form_values(attribute)
    elif attribute_value_type == 'position':
        attribute_form_values = get_positions_attr_form_values(attribute)
    return attribute_form_values

def get_simple_text_attr_form_values(attribute):
    subform_values = []
    subform_values.append(attribute.values.all()[0].text)
    return subform_values

def get_complex_text_attr_form_values(attribute):
    subform_values = []
    selection_mode = None
    separator = None
    text_field_value = prepare_lemmas_text(attribute)
    if attribute.selection_mode:
        selection_mode = attribute.selection_mode.pk
    subform_values.append(selection_mode)
    if attribute.separator:
        separator = attribute.separator.pk
    subform_values.append(separator)
    subform_values.append(text_field_value)
    return subform_values

def prepare_lemmas_text(attribute):
    separator_symbol = ''
    if attribute.separator:
        separator_symbol = attribute.separator.symbol
    values = attribute.values.order_by('text')
    values_text_reps = [value.text.strip("'") for value in values]
    return u'%s' % (separator_symbol.join(values_text_reps))

def get_simple_parameter_attr_form_values(attribute):
    subform_values = []
    parameter = attribute.values.all()[0].parameter
    param_model = parameter.type
    subparameters = parameter.subparameters.order_by('name')
    subform_values.append(param_model.pk)
    for subparam in subparameters.all():
        subform_values.append(subparam.pk)
    return subform_values

def get_complex_parameter_attr_form_values(attribute):
    subform_values = []
    subform_values.append(attribute.selection_mode.pk)
    subform_values.append(attribute.separator.pk)
    ordered_values = attribute.values.order_by('parameter__type__priority')
    subform_values.extend([value.parameter.type.pk for value in ordered_values])
    return subform_values

def get_simple_argument_attr_form_values(attribute):
    subform_values = []
    subform_values.append(attribute.values.all()[0].argument.id)
    return subform_values

def get_complex_argument_attr_form_values(attribute):
    subform_values = []
    subform_values.append(attribute.selection_mode.pk)
    subform_values.append(attribute.separator.pk)
    arguments = [value.argument for value in attribute.values.all()]
    sorted_arguments = sortArguments(arguments)
    arguments_ids = [arg.id for arg in sorted_arguments]
    subform_values.extend(arguments_ids)
    return subform_values

def get_positions_attr_form_values(attribute):
    subform_values = []
    subform_values.append(attribute.selection_mode.pk)
    subform_values.append(attribute.separator.pk)
    positions = [value.position for value in attribute.values.all()]
    sorted_positions = sortPositions(positions)
    positions_ids = [pos['position'].id for pos in sorted_positions]
    subform_values.extend(positions_ids)
    return subform_values
    
def create_argument_form(arg_model, subforms_values=[], 
                            pos=None, phraseologic=False, form_type='standard'):#,
                            #lex_arg_choosing=False):
    attr_sheets = []
    type_form = create_type_form(pos, arg_model, phraseologic, 
                                 form_type)#, lex_arg_choosing)
    if arg_model:
        attr_sheets = create_attributes_forms(pos, arg_model, subforms_values, form_type)
    return type_form, attr_sheets

def get_argument_model(arg_form_dict, phraseologic_modification=False):
    arg_model = None
    if arg_form_dict and arg_form_dict['arg_id']:
        arg_obj = Argument.objects.get(id=arg_form_dict['arg_id'])
        arg_model = Argument_Model.objects.get(arg_model_name=arg_obj.type)
    elif arg_form_dict and arg_form_dict['arg_type']:
        arg_model = Argument_Model.objects.get(arg_model_name=arg_form_dict['arg_type'])
    if phraseologic_modification:
        arg_model = change_to_phraseologic_model(arg_model)
    if arg_model and not arg_model.active:
        arg_model = None
    return arg_model

def change_to_phraseologic_model(arg_model):
    if arg_model and not arg_model.phraseologic:
        phraseologic_arg_models = Argument_Model.objects.filter(phraseologic=True, 
                                                                active=True).order_by('priority')
        arg_model = phraseologic_arg_models.all()[0]
    return arg_model
 
def create_propositions_form(arg_form_dict, position_arguments): 
    selected_argument = None
    if 'freq_coordinated' in arg_form_dict.keys():
        selected_argument = arg_form_dict['freq_coordinated']
    freq_coor_choices = coor_arguments_query(position_arguments)
    return ArgPropositionsForm(freq_coor_choices=freq_coor_choices,
                               sel_argument=selected_argument)
  
def coor_arguments_query(args):
    coordinated_arguments = Argument.objects.none()
    positions = Position.objects.annotate(arg_count=Count('arguments'))       
    q_args = []
    sorted_args = []
    for arg in args:
        arg_obj = jsArgToObj(arg) 
        q_args.append(Q(pk=arg_obj.pk))
        positions = positions.filter(arguments=arg_obj)
    positions = positions.filter(arg_count__gt=len(args))
    for position in positions:   
        coordinated_arguments = coordinated_arguments | position.arguments.all()
    coordinated_arguments = coordinated_arguments.exclude(reduce(or_, q_args)).distinct()
    # exclude arguments with inactive type (lexnp, preplexnp)
    coordinated_arguments = exclude_inactive_arguments(coordinated_arguments)
    for arg in coordinated_arguments:
        occurr = arg.positions.filter(id__in=positions.values_list('id')).aggregate(Sum('occurrences'))['occurrences__sum']
        if occurr != 0:
            sorted_args.append({'arg': arg,
                                'occurrences': occurr})
    sorted_args = sorted(sorted_args, key=itemgetter('occurrences'), reverse=True)
    args_tup_ls = list((arg['arg'].pk, '%s: %d' % (arg['arg'].text_rep, arg['occurrences'])) 
                       for arg in sorted_args)
    return args_tup_ls 

def exclude_inactive_arguments(arguments_query):
    inactive_models = Argument_Model.objects.filter(active=False).all()
    inactive_models_names = [arg_model.arg_model_name for arg_model in inactive_models]
    arguments_query = arguments_query.exclude(type__in=inactive_models_names)
    return arguments_query
    
def create_type_form(pos, arg_model, phraseologic_modification=False, 
                       form_type='standard'):#, lex_arg_choosing=False):
    type_form = None
    label = 'Typ'
    label = create_label(label)
    model_queryset = Argument_Model.objects.filter(active=True)
    if form_type == 'realization_main_arg':
        model_queryset = model_queryset.filter(has_realizations=True)
    elif form_type == 'standard':
        model_queryset = model_queryset.exclude(realization_only=True)
    model_queryset = model_queryset.order_by('priority')
    if form_type == 'lex_arg_choosing': #lex_arg_choosing:
        model_queryset = model_queryset.filter(used_in_lex=True)
    elif form_type == 'realization_arg':
        pass
    else:
        model_queryset = model_queryset.exclude(lex_only=True)
    if arg_model and phraseologic_modification:
        model_queryset = model_queryset.filter(phraseologic=True).order_by('priority')
        #phraseologic_equivalents = arg_model.phraseologic_equivalents.order_by('priority')
        if not arg_model.phraseologic:
            arg_model = model_queryset.all()[0]
        type_form = AddArgumentForm(value=arg_model, 
                                    model_queryset=model_queryset,
                                    empty_label=True, lemma_pos=pos,
                                    label=label)
    elif arg_model and not phraseologic_modification:
        type_form = AddArgumentForm(value=arg_model,
                                    model_queryset=model_queryset,
                                    lemma_pos=pos,
                                    label=label)
    elif not arg_model and phraseologic_modification:
        model_queryset = model_queryset.filter(phraseologic=True).order_by('priority')
        type_form = AddArgumentForm(lemma_pos=pos,
                                    model_queryset=model_queryset,
                                    label=label)
    else:
        type_form = AddArgumentForm(lemma_pos=pos,
                                    model_queryset=model_queryset,
                                    label=label)
    return type_form

def create_attributes_forms(pos, arg_model, subforms_values, form_type='standard'):
    sheets = []
    attribute_models = get_attribute_models(arg_model, subforms_values)
    param_models_to_exclude = get_parameter_types_to_exclude(arg_model)
    for i in range(len(attribute_models)):
        attr_form_values = []
        attribute_model = attribute_models[i]
        if i < len(subforms_values):
            attr_form_values = subforms_values[i]
        #if attribute_model not in attr_models_to_exclude:
        sheet = create_attribute_form(pos=pos, 
                                      selected_values=attr_form_values, 
                                      attribute_model=attribute_model, 
                                      param_models_to_exclude=param_models_to_exclude,
                                      form_type=form_type,
                                      arg_model=arg_model)
        sheets.append(sheet)
    return sheets

def create_attribute_form(pos, attribute_model, 
                             param_models_to_exclude, 
                             selected_values, form_type,
                             arg_model):
    arguments = []
    form = None
    positions = []
    label = create_label(attribute_model.atr_model_name)
    order = attribute_model.entry_edit_values_order
    #possible_attribute_values = attribute_model.atribute_values.exclude(id__in=attr_values_to_exclude)
    possible_parameter_models = attribute_model.possible_parameters.filter(active=True).exclude(id__in=param_models_to_exclude)
    hide_add_arg_button = False
    add_realization_button = False
    if arg_model.has_realizations:
        hide_add_arg_button = True
    if form_type == 'realization_arg':
        order = attribute_model.realizations_values_order
    elif form_type == 'standard':
        order = attribute_model.entry_edit_values_order
        possible_parameter_models = possible_parameter_models.exclude(realization_only=True)
    elif (form_type == 'position_or_arguments_arg_choosing' or form_type == 'lex_arg_choosing'):
        hide_add_arg_button = False;
        if arg_model.all_attr_params_used_in_lex:
            order = attribute_model.entry_edit_values_order
        else:
            possible_parameter_models = possible_parameter_models.exclude(realization_only=True)
        
    attribute_type = attribute_model.type.sym_name
    selection_modes = attribute_model.values_selection_modes.order_by('priority')
    value_separators = attribute_model.value_separators.order_by('priority')
    is_selection_mode_always_empty = False
    
    if attribute_type == 'text' and not selection_modes.exists():
        if not selected_values or len(selected_values) < 1:
            selected_values = ['']
        selected_attribute_value = selected_values[0].replace("\'", '')
        form = AtributeTextForm(label=label, value=selected_attribute_value)
    elif attribute_type == 'text' and selection_modes.exists():
        if not selected_values or len(selected_values) < 3:
            selected_values = [selection_modes.all()[0].pk, None, '']
        if selected_values[0]:
            selection_mode = AttrValueSelectionMode.objects.get(pk=selected_values[0])
            is_selection_mode_always_empty = selection_mode.always_empty
        form = TextAttrMultiValueForm(label=label, 
                                      selection_mode_queryset=selection_modes,
                                      value_separator_queryset=value_separators,
                                      selection_mode=selected_values[0], 
                                      value_separator=selected_values[1], 
                                      lemmas_text=selected_values[2])
    elif attribute_type == 'parameter' and not selection_modes.exists():
        sel_subparameters = []
        subparameters = AttributeSubparameter.objects.none()
        if not selected_values or len(selected_values) < 1:
            selected_values = [None]
        if selected_values[0]:
            param_model = AttributeParameterModel.objects.get(id=selected_values[0])
            subparameters = param_model.possible_subparams.order_by('name')
        if len(selected_values) > 1:
            sel_subparameters = selected_values[1:]
        form = AtributeChoiceForm(values=possible_parameter_models.order_by(order), 
                                  label=label, lemma_pos=pos, value=selected_values[0],
                                  subparameters=subparameters,
                                  sel_subparameters=sel_subparameters)
    elif attribute_type == 'parameter' and selection_modes.exists():
        if not selected_values or len(selected_values) < 3:
            selected_values = [selection_modes.all()[0].pk, None, None]
        if selected_values[0]:
            selection_mode = selection_modes.get(pk=selected_values[0])
            is_selection_mode_always_empty = selection_mode.always_empty
        form = ValueAttrMultiValueForm(label=label,
                                       selection_mode_queryset=selection_modes,
                                       value_separator_queryset=value_separators,
                                       values_queryset=possible_parameter_models.order_by(order),
                                       selection_mode=selected_values[0], 
                                       value_separator=selected_values[1], 
                                       values=selected_values[2:])
    elif attribute_type == 'argument' and not selection_modes.exists():
        form = SelectArgumentForm(label=label)
        if selected_values and selected_values[0]:
            try:
                arguments.append(Argument.objects.get(id=selected_values[0])) 
            except ValueError:
                pass
    elif attribute_type == 'argument' and selection_modes.exists():
        base_arg = None
        selection_mode = None
        if not selected_values or len(selected_values) < 2:
            selected_values = [selection_modes.all()[0].pk, None]
        if selected_values[0]:
            selection_mode = AttrValueSelectionMode.objects.get(pk=selected_values[0])
            is_selection_mode_always_empty = selection_mode.always_empty
        if arg_model.has_realizations and selection_mode: # TODO troche nagiete to szukanie base arg, malo generyczne i jakis arg moze jeszcze nie istniec na bazie
            try:
                base_arg_text_rep = u'%s(%s)' % (arg_model.arg_model_name, selection_mode.name)
                base_arg = Argument.objects.get(text_rep=base_arg_text_rep)
                add_realization_button = True
            except Argument.DoesNotExist:
                pass
        sel_realizations = []
        for argument_id in selected_values[2:]:
            try:
                argument = Argument.objects.get(id=argument_id)
                if base_arg and base_arg.realizations.filter(argument=argument).exists():
                    sel_realizations.append(argument)
                else:
                    arguments.append({'id': argument.id,
                                      'text_rep': argument.text_rep})
            except ValueError:
                pass
        form = ArgumentsForm(label=label, 
                             selection_mode_queryset=selection_modes,
                             value_separator_queryset=value_separators,
                             selection_mode=selected_values[0],
                             value_separator=selected_values[1],
                             base_arg=base_arg,
                             form_type=form_type,
                             sel_realizations=sel_realizations)
    elif attribute_type == 'position' and selection_modes.exists():
        if not selected_values or len(selected_values) < 2:
            selected_values = [selection_modes.all()[0].pk, None]
        if selected_values[0]:
            selection_mode = AttrValueSelectionMode.objects.get(pk=selected_values[0])
            is_selection_mode_always_empty = selection_mode.always_empty
        form = PositionsForm(label=label, 
                             value_separator_queryset=value_separators,
                             selection_mode_queryset=selection_modes,
                             selection_mode=selected_values[0], 
                             value_separator=selected_values[1])
        for position_id in selected_values[2:]:
            try:
                position = Position.objects.get(id=position_id)
                positions.append({'id': position.id,
                                  'text_rep': position.text_rep})
            except ValueError:
                pass
    sheet = {'form': form,
             'arguments': arguments,
             'positions': positions,
             'is_empty_selection_mode': is_selection_mode_always_empty,
             'hide_add_arg_button': hide_add_arg_button,
             'add_realization_button': add_realization_button}        
    return sheet

def create_label(label):
    label = '%s' % label
    return label

def get_parameter_types_to_exclude(arg_model):
    return [param_model.id for param_model in arg_model.exclude_param_models.all()]

############################## validation ##################################
def is_correct_lemma(argument_model, attribute_model, lemma):
    correct_form = False
    possible_pos_tags = argument_model.get_possible_lemma_tags(attribute_model)
    for interp in MORFEUSZ2.analyse(lemma.encode('utf8')):
        if (base_form_correct(interp, lemma) and
            pos_tag_correct(interp, possible_pos_tags)):
            correct_form = True
            break
        elif is_morfeusz_exception(lemma, possible_pos_tags):
            correct_form = True
            break
        elif attribute_model.value_can_be_number and lemma.isdigit():
            correct_form = True
            break
    return correct_form

def base_form_correct(interp, lemma):
    if interp.lemma.split(':')[0] == lemma:
        return True
    return False

def pos_tag_correct(interp, possible_pos_tags):
    tagstr = interp.getTag(MORFEUSZ2)
    pos_tag = tagstr.split(':')[0]
    if possible_pos_tags.filter(name=pos_tag).exists():
        return True
    return False

def contains_separator(lemma):
    contains_separator = False
    results_iter = MORFEUSZ2.analyse_iter(lemma.encode('utf8'))
    if len(lemma.split()) > 1:
        contains_separator = True
    else:
        while results_iter.hasNext():
            tagstr = results_iter.peek().getTag(MORFEUSZ2)
            pos_tag = tagstr.split(':')[0] 
            if pos_tag == 'interp':
                contains_separator = True
                break
            results_iter.next()
    return contains_separator

def is_preposition_case_pair_valid(preposition_obj, case_obj):
    # postp is used by prepadjp
    case_str = unicode(case_obj)
    prep_str = unicode(preposition_obj).split()[-1]
    if case_str != 'postp':
        # str is used by prepadjp
        if case_str == 'str':
            pcase = ['nom', 'acc']
        else:
            pcase = [case_str]
        for case in pcase:
            for interp in MORFEUSZ2.analyse(prep_str.encode('utf8')):
                tagstr = interp.getTag(MORFEUSZ2)
                tag_parts = tagstr.split(':')
                if len(tag_parts) > 1:
                    interp_pos = tag_parts[0]
                    interp_cases = tag_parts[1].split('.')
                    if interp_pos == 'prep' and case in interp_cases:
                        return True
        return False
    return True

############################################################################
############################## Submit form ##################################
############################################################################

@ajax(method='post')
def argument_form_submit(request, form_data):
    form_dict = dict((x['name'], x['value']) for x in form_data)
    error = validate_argument_form(form_dict)
    if error:
        raise AjaxError(error)
    if not form_dict['freq_coordinated']:
        arg_obj = get_argument_from_form(form_dict['arg_type'], 
                                         form_dict['subform_values'])
    else:
        arg_obj = Argument.objects.get(id=form_dict['freq_coordinated'])
    return {'id': arg_obj.id,
            'text_rep': arg_obj.text_rep,
            'type': arg_obj.type}
  
def validate_argument_form(form_dict):
    error = ''
    if not all_fields_filled(form_dict):
        error = u'Wypełnij wszystkie niezbędne pola formularza.'
    if not error and not form_dict['freq_coordinated']:
        error = validate_argument_form_fields(form_dict)
    return error
  
def all_fields_filled(form_dict):
    if not form_dict['freq_coordinated'] and not form_dict['arg_type']:
        return False
    for attr_subform in form_dict['subform_values']:
        for attr_value in attr_subform:
            if not attr_value: 
                return False
    return True
    
def validate_argument_form_fields(form_dict):
    error = ''
    arg_model_obj = Argument_Model.objects.get(arg_model_name=form_dict['arg_type'])
    attribute_models = get_attribute_models(arg_model_obj, form_dict['subform_values'])
    lexicalized_arg_model = get_lexicalized_arg_model(arg_model_obj, form_dict['subform_values'])
    for i in range(len(attribute_models)):
        error = validate_attribute_subform(lexicalized_arg_model,
                                           attribute_models[i], 
                                           form_dict['subform_values'][i], 
                                           form_dict['lex_arg_choosing'])
        if error:
            break
    if not error:
        error = preposition_validation(attribute_models, form_dict['subform_values'])
    return error

def get_lexicalized_arg_model(main_arg_model, attrs_subforms):
    lexicalized_arg_model = None
    attribute_models = main_arg_model.atribute_models.order_by('priority')
    if attribute_models.filter(atr_model_name=u'TYP FRAZY').exists():   
        for i in range(len(attribute_models)):
            if attribute_models[i].atr_model_name == u'TYP FRAZY' and len(attrs_subforms) > i:
                try:
                    argument = Argument.objects.get(id=attrs_subforms[i][0])
                    lexicalized_arg_model = argument.model_used_for_limitations()
                except ValueError: 
                    pass
                except IndexError: # index error, zeby dalo sie edytowac stare fixy
                    pass
                break
    return lexicalized_arg_model 

def preposition_validation(attribute_models, attr_subform_values):
    error = ''
    prep, case = find_preposition_case_pair(attribute_models, attr_subform_values)
    if prep and case and not is_preposition_case_pair_valid(prep, case):
        error = preposition_case_pair_error()
    return error
        
def find_preposition_case_pair(attribute_models, attr_subform_values):
    case = None
    prep = None
    for i in range(len(attribute_models)):
        if attribute_models[i].sym_name == 'case':
            case = attribute_models[i].possible_parameters.get(pk=attr_subform_values[i][0])
        elif attribute_models[i].sym_name == 'preposition':
            prep = attribute_models[i].possible_parameters.get(pk=attr_subform_values[i][0])
        if case and prep:
            break
    return prep, case

def preposition_case_pair_error():
    return u'Przypadek nie jest zgodny z przyimkiem.'

def validate_attribute_subform(argument_model, attribute_model, 
                                  attr_subform_values, lex_arg_choosing):
    error = ''
    attribute_type = attribute_model.type.sym_name
    selection_modes = attribute_model.values_selection_modes
    if attribute_type == 'text' and selection_modes.exists():
        error = validate_complex_text_attr(argument_model, attribute_model, attr_subform_values)
    elif attribute_type == 'parameter' and selection_modes.exists():
        error = validate_complex_parameter_attr(attribute_model, attr_subform_values)
    elif attribute_type == 'argument' and not selection_modes.exists():
        error = validate_simple_argument_attr(attribute_model, attr_subform_values)
    elif attribute_type == 'argument' and selection_modes.exists():
        error = validate_complex_argument_attr(attribute_model, attr_subform_values, lex_arg_choosing)
    return error

def strip_quotation_marks(value):
    value = value.strip()
    value = value.strip('\"\'')
    value = value.strip()
    return value

def wrong_base_form_error(value):
    return u'Lemat %s jest nieprawidłowy.' % value

def validate_complex_text_attr(argument_model, attribute_model, attr_subform_values):
    error = ''
    separator = attribute_model.value_separators.get(pk=attr_subform_values[1])
    text_field_content = attr_subform_values[2]
    if not is_empty_lemma(text_field_content):
        text_values = text_field_content.split(separator.symbol)
        for text_value in text_values:
            text_value = strip_quotation_marks(text_value)
            if contains_separator(text_value):
                error = separator_error(attribute_model)
                break
            if not is_correct_lemma(argument_model, attribute_model, text_value):
                error = wrong_base_form_error(text_value)
                break
    return error

def is_empty_lemma(text_field_value):
    is_empty_lemma = False
    text_field_value = strip_quotation_marks(text_field_value)
    empty_lemma_parts = text_field_value.split('(')
    if len(empty_lemma_parts) == 2:
        type = empty_lemma_parts[0]
        gender_str = empty_lemma_parts[1].rstrip(')')
        genders = gender_str.split('.')
        if type == 'E' and are_gender_values(genders):
            is_empty_lemma = True            
    return is_empty_lemma

def are_gender_values(genders):
    are_gender_values = True
    gender_model = Atribute_Model.objects.get(sym_name='gender')
    for gender in genders:
        if not gender_model.possible_parameters.filter(name=gender).exists():
            are_gender_values = False
            break
    return are_gender_values
    
def separator_error(attribute_model):
    attribute_model_name = attribute_model.atr_model_name
    return u'W polu tekstowym %s wykorzystano zły separator wartości.' % attribute_model_name

def validate_complex_parameter_attr(attribute_model, attr_subform_values):
    error = ''
    if len(attr_subform_values) < 3:
        error = no_parameter_selection_error(attribute_model)
    return error

def no_parameter_selection_error(attribute_model):
    attribute_model_name = attribute_model.atr_model_name
    return u'Zaznacz co najmniej jeden parametr określający %s.' % attribute_model_name

def validate_simple_argument_attr(attribute_model, attr_subform_values):
    error = ''
    if len(attr_subform_values) >= 1:
        selected_arg = Argument.objects.get(id=attr_subform_values[0])
        selected_arg_model = Argument_Model.objects.get(arg_model_name=selected_arg.type)
        if selected_arg_model.use_subarg_for_limitations:
            if not selected_arg.atributes.count() == 1:
                error = lex_nesting_error(selected_arg)
            elif not selected_arg.atributes.all()[0].values.count() == 1:
                error = lex_nesting_error(selected_arg)
            else:
                lexicalized_subarg = selected_arg.atributes.all()[0].values.all()[0].argument
                lexicalized_subarg_model = Argument_Model.objects.get(arg_model_name=lexicalized_subarg.type)
                if not lexicalized_subarg_model.used_in_lex or not lexicalized_subarg_model.active:
                    error = lex_nesting_error(selected_arg)
    return error

def validate_complex_argument_attr(attribute_model, attr_subform_values, lex_arg_choosing):
    error = ''
    attr_selection_mode = attribute_model.values_selection_modes.get(pk=attr_subform_values[0])
    if lex_arg_choosing and len(attr_subform_values) != 3:
        error = lex_arg_error()
    elif lex_arg_choosing and len(attr_subform_values) == 3:
        selected_arg = Argument.objects.get(id=attr_subform_values[2])
        selected_arg_model = Argument_Model.objects.get(arg_model_name=selected_arg.type) 
        if not selected_arg_model.used_in_lex or not selected_arg_model.active:
            error = not_lexicalizable_arg(selected_arg_model)
        elif selected_arg_model.use_subarg_for_limitations:
            error = lex_nesting_error(selected_arg)
    elif (not lex_arg_choosing and len(attr_subform_values) > 2 and 
          attribute_model.need_lexicalized_values and
          not is_phraseologic_arg_defined(attr_subform_values[2:])):
        error = no_lex_arg_error(attribute_model)
    if not error:
        if (len(attr_subform_values) < 3 and 
            not (attr_selection_mode.always_empty or attr_selection_mode.can_be_empty)):
            error = no_arguments_error(attribute_model)
    return error

def lex_arg_error():
    return u'Wybierz jedno rozwinięcie typu frazy.'

def lex_nesting_error(argument):
    return u'Nie można zleksykalizować typu frazy %s.' % argument.text_rep

def not_lexicalizable_arg(argument_model):
    arg_model_name = argument_model.arg_model_name
    return u'Typy fraz %s nie podlegają leksykalizacji.' % arg_model_name

def is_phraseologic_arg_defined(arguments_ids):
    for arg_id in arguments_ids:
        selected_arg = Argument.objects.get(id=arg_id)
        if selected_arg.is_phraseologic():
            return True
    return False

def no_lex_arg_error(attribute_model):
    attribute_model_name = attribute_model.atr_model_name
    return u'Do pola %s musi zostać dodany co najmniej jeden zleksykalizowany typ frazy.' % attribute_model_name
 
def no_arguments_error(attribute_model):
    attribute_model_name = attribute_model.atr_model_name
    return u'Do pola %s musi zostać dodany co najmniej jeden typ frazy.' % attribute_model_name
  
def get_argument_from_form(arg_type, attrs_subforms):
    arg_model_obj = Argument_Model.objects.get(arg_model_name=arg_type)
    atribute_objs = get_or_create_attributes(arg_model_obj, attrs_subforms)
    arg_obj = get_or_create_argument(arg_model_obj, atribute_objs)
    return arg_obj

def get_or_create_argument(argument_model, sorted_attributes):
    arg_type = argument_model.arg_model_name
    atr_str_tab = [unicode(attr) for attr in sorted_attributes]
    arg_text_rep = ''
    if len(atr_str_tab) == 0:
        arg_text_rep = '%s' % (arg_type)
    elif argument_model.hide_type:
        arg_text_rep = '%s' % (','.join(atr_str_tab))
    else: 
        arg_text_rep = '%s(%s)' % (arg_type, ','.join(atr_str_tab))
    try:
        arg_obj = Argument.objects.get(text_rep=arg_text_rep)
    except Argument.DoesNotExist:
        arg_obj = Argument(type=arg_type, text_rep=arg_text_rep)
        arg_obj.save()
        arg_obj.atributes.add(*sorted_attributes)
    return arg_obj
    

def get_or_create_attributes(arg_model_obj, attrs_subforms):
    attribute_objs = []
    #attribute_models = arg_model_obj.atribute_models.order_by('priority')
    attribute_models = get_attribute_models(arg_model_obj, attrs_subforms)
    for i in range(len(attribute_models)):
        #if not attribute_models[i] in attr_models_to_exclude:
        attribute_type = attribute_models[i].type.sym_name
        selection_modes = attribute_models[i].values_selection_modes
        attr_subform_values = attrs_subforms[i]
        if attribute_type == 'text' and not selection_modes.exists(): 
            attribute_objs.append(get_or_create_simple_text_attr(attribute_models[i], attr_subform_values))
        elif attribute_type == 'text' and selection_modes.exists():
            attribute_objs.append(get_or_create_complex_text_attr(attribute_models[i], attr_subform_values))
        elif attribute_type == 'parameter' and not selection_modes.exists():
            attribute_objs.append(get_or_create_simple_parameter_attr(attribute_models[i], attr_subform_values))
        elif attribute_type == 'parameter' and selection_modes.exists():
            attribute_objs.append(get_or_create_complex_parameter_attr(attribute_models[i], attr_subform_values))
        elif attribute_type == 'argument' and not selection_modes.exists():
            attribute_objs.append(get_or_create_simple_argument_attr(attribute_models[i], attr_subform_values))
        elif attribute_type == 'argument' and selection_modes.exists():
            attribute_objs.append(get_or_create_complex_argument_attr(attribute_models[i], attr_subform_values))
        elif attribute_type == 'position':
            attribute_objs.append(get_or_create_positions_attr(attribute_models[i], attr_subform_values))
    return attribute_objs

def get_attribute_models(arg_model, attrs_subforms):
    attribute_models = arg_model.atribute_models.order_by('priority')
    if attribute_models.filter(atr_model_name=u'TYP FRAZY').exists():       
        for i in range(len(attribute_models)):
            if attribute_models[i].atr_model_name == u'TYP FRAZY' and len(attrs_subforms) > i:
                try:
                    argument = Argument.objects.get(id=attrs_subforms[i][0])
                    attr_models_to_exclude = get_attr_models_to_exclude(argument)
                    attribute_models = attribute_models.exclude(pk__in=attr_models_to_exclude)
                    attribute_models = attribute_models.order_by('priority')
                except ValueError: 
                    pass
                except IndexError: # index error, zeby dalo sie edytowac stare fixy
                    pass
                break
    return attribute_models   

def get_or_create_simple_text_attr(attribute_model, attr_subform_values):
    attr_value = prepare_text_attr_value(attr_subform_values[0])
    attr_val_obj = get_or_create_text_attr_value(attr_value)
    attr_obj = get_or_create_attribute(attribute_model=attribute_model, 
                                       values=[attr_val_obj],
                                       selection_mode=None, 
                                       separator=None) 
    return attr_obj

def prepare_text_attr_value(value):
    value = strip_quotation_marks(value)
    value = "\'%s\'" % value
    return value

def get_or_create_complex_text_attr(attribute_model, attr_subform_values):
    values = []
    selection_mode = attribute_model.values_selection_modes.get(pk=attr_subform_values[0])
    separator = attribute_model.value_separators.get(pk=attr_subform_values[1])
    text_field_value = attr_subform_values[2]
    if is_empty_lemma(text_field_value):
        empty_lemma_value = correct_empty_lemma_value(text_field_value)
        empty_lemma_value = prepare_text_attr_value(empty_lemma_value)
        values.append(get_or_create_text_attr_value(empty_lemma_value))
    else:
        text_values = attr_subform_values[2].split(separator.symbol)
        for text_value in text_values:
            text_value = prepare_text_attr_value(text_value)
            values.append(get_or_create_text_attr_value(text_value))
    # nie ma wyboru listowego kiedy mamy tylko jeden lemat
    if len(values) < 2:
        selection_mode = None
        separator = None
    attr_obj = get_or_create_attribute(attribute_model=attribute_model, 
                                       values=values, 
                                       selection_mode=selection_mode, 
                                       separator=separator)
    return attr_obj

def correct_empty_lemma_value(empty_lemma_value):
    empty_lemma_value = strip_quotation_marks(empty_lemma_value)
    empty_lemma_parts = empty_lemma_value.split('(')
    type = empty_lemma_parts[0]
    gender_str = empty_lemma_parts[1].rstrip(')')
    genders = gender_str.split('.')    
    sorted_genders = sort_genders(genders)   
    empty_lemma_value = u'E(%s)' % '.'.join(sorted_genders)  
    return empty_lemma_value

def sort_genders(genders):
    gender_model = Atribute_Model.objects.get(sym_name='gender')
    genders = [param_model.name for param_model in gender_model.possible_parameters.order_by('priority') 
                if param_model.name in genders]
    return genders

def get_or_create_simple_parameter_attr(attribute_model, attr_subform_values):
    attr_val_obj = get_parameter_attr_value_from_subform(attr_subform_values)
    attr_obj = get_or_create_attribute(attribute_model=attribute_model, 
                                       values=[attr_val_obj], 
                                       selection_mode=None,
                                       separator=None)
    return attr_obj

def get_parameter_attr_value_from_subform(attr_subform_values):
    param_model = AttributeParameterModel.objects.get(pk=attr_subform_values[0])
    subparam_objs = [AttributeSubparameter.objects.get(pk=subparam_pk) 
                     for subparam_pk in attr_subform_values[1:]]
    param_obj, xx = get_or_create_attr_parameter(param_model, subparam_objs)
    attr_val_obj, xx = get_or_create_parameter_attr_value(param_obj)
    return attr_val_obj 

def get_or_create_complex_parameter_attr(attribute_model, attr_subform_values):
    selection_mode = attribute_model.values_selection_modes.get(pk=attr_subform_values[0])
    separator = attribute_model.value_separators.get(pk=attr_subform_values[1])
    param_models_pks = attr_subform_values[2:]
    values = [get_parameter_attr_value_from_subform([pk]) for pk in param_models_pks]
    attr_obj = get_or_create_attribute(attribute_model=attribute_model, 
                                       values=values, 
                                       selection_mode=selection_mode, 
                                       separator=separator)
    return attr_obj

def get_or_create_positions_attr(attribute_model, attr_subform_values):
    values = []
    selection_mode = attribute_model.values_selection_modes.get(pk=attr_subform_values[0])
    separator = attribute_model.value_separators.get(pk=attr_subform_values[1])
    positions_ids = attr_subform_values[2:]
    for pos_id in positions_ids:
        position = Position.objects.get(id=pos_id)
        values.append(get_or_create_position_attr_value(position))
    attr_obj = get_or_create_attribute(attribute_model=attribute_model, 
                                       values=values, 
                                       selection_mode=selection_mode, 
                                       separator=separator)
    return attr_obj

def get_or_create_simple_argument_attr(attribute_model, attr_subform_values):
    argument = Argument.objects.get(id=attr_subform_values[0])
    value = get_or_create_argument_attr_value(argument)
    attr_obj = get_or_create_attribute(attribute_model=attribute_model, 
                                       values=[value], 
                                       selection_mode=None, 
                                       separator=None)
    return attr_obj

def get_or_create_complex_argument_attr(attribute_model, attr_subform_values):
    values = []
    selection_mode = attribute_model.values_selection_modes.get(pk=attr_subform_values[0])
    separator = attribute_model.value_separators.get(pk=attr_subform_values[1])
    arguments_ids = attr_subform_values[2:]
    for arg_id in arguments_ids:
        argument = Argument.objects.get(id=arg_id)
        values.append(get_or_create_argument_attr_value(argument))
    attr_obj = get_or_create_attribute(attribute_model=attribute_model, 
                                       values=values, 
                                       selection_mode=selection_mode, 
                                       separator=separator)
    return attr_obj
    
def get_or_create_text_attr_value(value):
    text_type = AttributeType.objects.get(sym_name='text')
    try:
        attr_val_obj = Atribute_Value.objects.get(text=value, type=text_type)
    except:
        attr_val_obj = Atribute_Value(text=value, priority=1000,
                                      type=text_type)
        attr_val_obj.save()
    return attr_val_obj

def get_or_create_argument_attr_value(argument):
    argument_type = AttributeType.objects.get(sym_name='argument')
    try:
        attr_val_obj = Atribute_Value.objects.get(argument=argument,
                                                  type=argument_type)
    except:
        attr_val_obj = Atribute_Value(argument=argument, priority=2000,
                                      type=argument_type)
        attr_val_obj.save()
    return attr_val_obj

def get_or_create_position_attr_value(position):
    position_type = AttributeType.objects.get(sym_name='position')
    try:
        attr_val_obj = Atribute_Value.objects.get(position=position,
                                                  type=position_type)
    except:
        attr_val_obj = Atribute_Value(position=position, priority=3000,
                                      type=position_type)
        attr_val_obj.save()
    return attr_val_obj