from django.forms.models import model_to_dict, fields_for_model
from django.forms.models import  ModelForm, ModelFormMetaclass, modelform_factory, model_to_dict
from django.forms.util import ErrorList, ErrorDict
from django.core.exceptions import NON_FIELD_ERRORS
from simple_translation.translation_pool import translation_pool

class TranslationModelFormMetaclass(ModelFormMetaclass):
    
    def __new__(cls, name, bases, attrs):
        formfield_callback = attrs.get('formfield_callback', None)
        try:
            parents = [b for b in bases if issubclass(b, TranslationModelForm)]
        except NameError:
            # We are defining TranslationModelForm itself.
            parents = None
            
        new_class = super(TranslationModelFormMetaclass, cls).__new__(cls, name, bases,
                attrs)
        if not parents:
            return new_class
            
        opts = new_class._meta
        if opts.model:
            if translation_pool.is_registered(opts.model):
                info = translation_pool.get_info(opts.model)
                translated_model = info.translated_model
                new_class.child_form_class = modelform_factory(translated_model,
                    exclude=[info.translation_of_field], formfield_callback=formfield_callback)
                new_class.declared_fields.update(new_class.child_form_class.declared_fields)
                new_class.base_fields.update(new_class.child_form_class.base_fields)
        return new_class
        
class TranslationModelForm(ModelForm):
    __metaclass__ = TranslationModelFormMetaclass
    
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
        initial={}, error_class=ErrorList, label_suffix=':',
        empty_permitted=False, instance=None):
        
        model = self._meta.model
        child_model = self.child_form_class._meta.model
        info = translation_pool.get_info(model)
        
        current_language = self.base_fields[info.language_field].initial

        if instance and instance.pk:
            try:
                child_instance = child_model.objects.get(**{
                    info.translation_of_field: instance.pk,
                    info.language_field: current_language})
            except child_model.DoesNotExist:
                child_instance = child_model(**{
                    info.language_field: current_language})
        else:
            child_instance = child_model(**{info.language_field: current_language})
            
        initial.update(model_to_dict(child_instance))
        self.child_form = self.child_form_class(data=data, files=files, auto_id=auto_id, prefix=prefix,
            initial=initial, error_class=error_class, label_suffix=label_suffix,
            empty_permitted=empty_permitted, instance=child_instance)
            
        
        super(TranslationModelForm, self).__init__(data=data, files=files, auto_id=auto_id, prefix=prefix,
            initial=initial, error_class=error_class, label_suffix=label_suffix,
            empty_permitted=empty_permitted, instance=instance)

    def full_clean(self):
        super(TranslationModelForm, self).full_clean()
        if self._errors:
            self.cleaned_data = {}
        self.child_form.full_clean()
        if self.child_form._errors:
            if not self._errors:
                self._errors = ErrorDict()
            for k, v in self.child_form._errors.items():
                if k in self._errors and k != NON_FIELD_ERRORS:
                    # remove existing errors so there are no duplicates
                    del self._errors[k]
            self._update_errors(self.child_form._errors)
            del self.cleaned_data

def translation_modelform_factory(model, form=TranslationModelForm, fields=None, exclude=None,
    formfield_callback=None):
    # Create the inner Meta class. FIXME: ideally, we should be able to
    # construct a ModelForm without creating and passing in a temporary
    # inner class.
    info = translation_pool.get_info(model)
    translated_model = info.translated_model
    translation_fields = [f[0].name for f in translated_model._meta.get_fields_with_model()]
    # Build up a list of attributes that the Meta object will have.
    attrs = {'model': model}
    if fields is not None:
        attrs['fields'] = [ field for field in fields if not field in translation_fields]
    if exclude is not None:
        attrs['exclude'] = exclude

    # If parent form class already has an inner Meta, the Meta we're
    # creating needs to inherit from the parent's inner meta.
    parent = (object,)
    if hasattr(form, 'Meta'):
        parent = (form.Meta, object)
    Meta = type('Meta', parent, attrs)

    # Give this new form class a reasonable name.
    class_name = model.__name__ + 'Form'

    # Class attributes for the new form class.
    form_class_attrs = {
        'Meta': Meta,
        'formfield_callback': formfield_callback
    }

    return TranslationModelFormMetaclass(class_name, (form,), form_class_attrs)
