from copy import copy, deepcopy
from django import forms
from django.core.serializers.json import DjangoJSONEncoder
from django.forms import fields
from django.forms.forms import BoundField
from django.forms.models import  ModelChoiceField, ModelMultipleChoiceField

class ExtJSONEncoder(DjangoJSONEncoder):
    """
    JSONEncoder subclass that knows how to encode django forms into ExtJS config objects.
    """

    CHECKBOX_EDITOR = {
        'xtype': 'checkbox'
    }
    COMBO_EDITOR = {
            #'listWidth': 'auto',
            'width': 150,
            'xtype': 'combo',
            #'xtype': 'jsoncombo',
        }
    DATE_EDITOR = {
            'xtype': 'datefield'
        }
    EMAIL_EDITOR = {
        'vtype':'email',
        'xtype': 'textfield'
    }
    NUMBER_EDITOR = {
        'xtype': 'numberfield'
    }
    NULL_EDITOR = {
        'fieldHidden': True,
        'xtype': 'textfield'
    }
    TEXT_EDITOR = {
        'xtype': 'textfield'
    }
    TIME_EDITOR = {
        'xtype': 'timefield'
    }
    URL_EDITOR = {
        'vtype':'url',
        'xtype': 'textfield'
    }

    CHAR_PIXEL_WIDTH = 8

    EXT_DEFAULT_CONFIG = {
        'editor': TEXT_EDITOR
        #'labelWidth': 300,
        #'autoWidth': True,
    }

    DJANGO_EXT_FIELD_TYPES = {
        fields.BooleanField: ["Ext.form.Checkbox", CHECKBOX_EDITOR],
        fields.CharField: ["Ext.form.TextField", TEXT_EDITOR],
        fields.ChoiceField: ["Ext.form.ComboBox", COMBO_EDITOR],
        fields.DateField: ["Ext.form.DateField", DATE_EDITOR],
        fields.DateTimeField: ["Ext.form.DateField", DATE_EDITOR],
        fields.DecimalField: ["Ext.form.NumberField", NUMBER_EDITOR],
        fields.EmailField: ["Ext.form.TextField", EMAIL_EDITOR],
        fields.IntegerField: ["Ext.form.NumberField", NUMBER_EDITOR],
        ModelChoiceField: ["Ext.form.ComboBox", COMBO_EDITOR],
        ModelMultipleChoiceField: ["Ext.form.ComboBox", COMBO_EDITOR],
        fields.MultipleChoiceField: ["Ext.form.ComboBox",COMBO_EDITOR],
        #NullField: ["Ext.form.TextField", NULL_EDITOR],
        fields.NullBooleanField: ["Ext.form.Checkbox", CHECKBOX_EDITOR],
        #BooleanField: ["Ext.form.Checkbox", CHECKBOX_EDITOR],
        fields.SplitDateTimeField: ["Ext.form.DateField", DATE_EDITOR],
        fields.TimeField: ["Ext.form.DateField", TIME_EDITOR],
        fields.URLField: ["Ext.form.TextField", URL_EDITOR],
    }

    EXT_DATE_ALT_FORMATS = 'm/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d'

    EXT_TIME_ALT_FORMATS = 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d'

    DJANGO_EXT_FIELD_ATTRS = {
        #Key: django field attribute name
        #Value: tuple[0] = ext field attribute name,
        #       tuple[1] = default value
        'choices': ['store', None],
        #'default': ['value', None],
        'fieldset': ['fieldSet', None],
        'help_text': ['helpText', None],
        'initial': ['value', None],
        #'input_formats': ['altFormats', None],
        'label': ['fieldLabel', None],
        'max_length': ['maxLength', None],
        'max_value': ['maxValue', None],
        'min_value': ['minValue', None],
        'name': ['name', None],
        'required': ['allowBlank', False],
        'size': ['width', None],
        'hidden': ['fieldHidden', False],
    }

    def default(self, o, form=None, field_name=None):
        if issubclass(o.__class__, (forms.Form,forms.BaseForm)):
            flds = []

            for name, field in o.fields.items():
                if isinstance(field, dict):
                    field['title'] = name
                else:
                    field.name = name
                cfg = self.default(field, o, name)
                flds.append(cfg)

            return flds
        elif isinstance(o, dict):
            #Fieldset
            default_config = {
                'autoHeight': True,
                'collapsible': True,
                'items': [],
                'labelWidth': 200,
                'title': o['title'],
                'xtype':'fieldset',
            }
            del o['title']

            #Ensure fields are added sorted by position
            for name, field in sorted(o.items()):
                field.name = name
                default_config['items'].append(self.default(field))
            return default_config
        elif issubclass(o.__class__, fields.Field):
            #bf = form and form.is_bound and BoundField(form, o, field_name) or None
            bf = BoundField(form, o, field_name)
            print field_name , o.__class__

            default_config = {}
            if o.__class__ in self.DJANGO_EXT_FIELD_TYPES:
                default_config.update(self.DJANGO_EXT_FIELD_TYPES[o.__class__][1])
            else:
                default_config.update(self.EXT_DEFAULT_CONFIG['editor'])
            config = deepcopy(default_config)
            if bf:
                config['invalidText']="".join(form[field_name].errors)

            if form and form.is_bound:
                data = bf.data
            else:
                if field_name:
                    data = form.initial.get(field_name, o.initial)
                    if callable(data):
                        data = data()
                else:
                    data = None
            config['value'] = data
            for dj, ext in self.DJANGO_EXT_FIELD_ATTRS.items():
                v = None
                if dj == 'size':
                    v = o.widget.attrs.get(dj, None)
                    if v is not None:
                        if o.__class__ in (fields.DateField, fields.DateTimeField,
                           fields.SplitDateTimeField, fields.TimeField):
                            v += 8
                        #Django's size attribute is the number of characters,
                        #so multiply by the pixel width of a character
                        v = v * self.CHAR_PIXEL_WIDTH
                elif dj == 'hidden':
                    v = o.widget.attrs.get(dj, default_config.get('fieldHidden', ext[1]))
                elif dj == 'name':
                    v = bf and bf.html_name or field_name
                elif dj == 'label':
                    v = bf and bf.label or getattr(o, dj, ext[1])
                elif getattr(o, dj, ext[1]) is None:
                    #print "dj:%s field name:%s"%(dj,field_name)
                    pass
                #elif dj == 'input_formats':
                    #alt_fmts = []
                    ##Strip out the '%'  placeholders
                    #for fmt in getattr(field, dj, ext[1]):
                        #alt_fmts.append(fmt.replace('%', ''))
                    #v = u'|'.join(alt_fmts)
                elif isinstance(ext[1], basestring):
                    v = getattr(o, dj, getattr(field, ext[1]))
                elif ext[0] == 'store':
                    v = {
                        'autoLoad': True,
                        'storeId': o.name,
                        'url': '/csds/ext/rdo/queryset/%s/' % (o.name.lower(),),
                        #'xtype': 'jsonstore',
                    }
                elif dj == 'required':
                    try:
                        v = not getattr(o, dj)
                    except AttributeError :
                        v = ext[1]
                else:
                    v = getattr(o, dj, ext[1])
                if v is not None:
                    if ext[0] == 'name':
                        config[ext[0]] = v
                        config['header'] = v
                    elif ext[0] not in ('name', 'dataIndex', 'fieldLabel', 'header', 'defaultValue'):
                    #elif ext[0] in ('allowBlank', 'listWidth', 'store', 'width'):
                            #if isinstance(v, QuerySetIterator):
                            #    config['editor'][ext[0]] = list(v)
                        config[ext[0]] = v
                        if ext[0] == 'store':
                            #config['url'] = v['url']
                            choices = [(c[0],c[1]) for c in o.choices]
                            config['store'] = choices
                            config['displayField'] = 'display'
                            config['editable'] = False
                            #config['editor']['forceSelection'] = True
                            config['hiddenName'] = o.name
                            #config['lastQuery'] = ''
                            config['mode'] = 'local'
                            config['triggerAction'] = 'all'
                            #config['valueField'] = 'id'

                    elif isinstance(v, unicode):
                        config[ext[0]] = v.encode('utf8')
                    else:
                        config[ext[0]] = v
            return config
        else:
            return super(ExtJSONEncoder, self).default(o)