import json

import jsonschema

from scrapi import registry
from scrapi.util import json_without_bytes


def strip_empty(schema, required):
    ''' Removes empty fields from the processed schema
    '''
    for k, v in schema.items():
        if k not in required:
            schema[k] = do_strip_empty(v)
            if not schema[k]:
                del schema[k]
    return schema


def do_strip_empty(value):
    ''' Filters empty values from container types
    '''
    return {
        dict: lambda d: dict([(k, do_strip_empty(v)) for k, v in d.items() if v]),
        list: lambda l: [do_strip_empty(v) for v in l if v],
        tuple: lambda l: [do_strip_empty(v) for v in l if v]
    }.get(type(value), lambda x: x or None)(value)


class BaseDocument(object):

    """
        For file objects. Automatically validates input to ensure
        compatibility with scrAPI.
    """

    schema = {}

    def __init__(self, attributes):
        # validate a version of the attributes that are safe to check
        # against the JSON schema
        attributes = json_without_bytes(strip_empty(attributes, self.schema.get('required', [])))
        jsonschema.validate(attributes, self.schema,
                            format_checker=jsonschema.FormatChecker())

        self.attributes = attributes

    def get(self, attribute, default=None):
        """
            Maintains compatibility with previous dictionary implementation of scrAPI
            :: str -> str
        """
        return self.attributes.get(attribute, default)

    def __getitem__(self, attr):
        return self.attributes[attr]

    def __setitem__(self, attr, val):
        self.attributes[attr] = val

    def __delitem__(self, attr):
        del self.attributes[attr]


class RawDocument(BaseDocument):

    @property
    def schema(self):
        return {
            '$schema': 'http://json-schema.org/draft-04/schema#',
            'type': 'object',
            'properties': {
                'doc': {
                    'type': 'string',
                    'description': 'The raw metadata'
                },
                'docID': {
                    'type': 'string',
                    'description': 'A service-unique identifier'
                },
                'source': {
                    'type': 'string',
                    'enum': [entry.short_name for entry in registry.values()],
                    'description': 'short_name for the source'
                },
                'filetype': {
                    'type': 'string',
                    'description': 'The format of the metadata (ie, xml, json)'
                }
            },
            'required': [
                'doc',
                'docID',
                'source',
                'filetype'
            ]
        }


class NormalizedDocument(BaseDocument):

    with open('normalized.json') as f:
        schema = json.load(f)

@@ -4,16 +4,28 @@
 ort json
+%0Aimport copy
 %0A%0Aimport
@@ -126,22 +126,24 @@
 p_empty(
-schema
+document
 , requir
@@ -144,16 +144,24 @@
 required
+=tuple()
 ):%0A    '
@@ -234,22 +234,24 @@
 k, v in 
-schema
+document
 .items()
@@ -294,22 +294,24 @@
         
-schema
+document
 %5Bk%5D = do
@@ -345,18 +345,158 @@
  if 
-not schema
+k == 'otherProperties':%0A                document%5Bk%5D = %5Bproperty for property in document%5Bk%5D if property.get('properties')%5D%0A            if not document
 %5Bk%5D:
@@ -516,22 +516,24 @@
     del 
-schema
+document
 %5Bk%5D%0A    
@@ -539,22 +539,100 @@
  return 
-schema
+document%0A%0A%0Adef strip_list(l):%0A    return filter(lambda x: x, map(do_strip_empty, l))
 %0A%0A%0Adef d
@@ -742,198 +742,72 @@
 ct: 
-lambda d: dict(%5B(k, do_strip_empty(v)) for k, v in d.items() if v%5D),%0A        list: lambda l: %5Bdo_strip_empty(v) for v in l if v%5D,%0A        tuple: lambda l: %5Bdo_strip_empty(v) for v in l if v%5D
+strip_empty,%0A        list: strip_list,%0A        tuple: strip_list
 %0A   
@@ -841,16 +841,8 @@
 x: x
- or None
 )(va
@@ -1015,44 +1015,422 @@
  %7B%7D%0A
-%0A    def __init__(self, attributes):
+    format_checker = jsonschema.FormatChecker()%0A%0A    def __init__(self, attributes, validate=True, clean=False):%0A        ''' Initializes a document%0A%0A            :param dict attributes: the dictionary representation of a document%0A            :param bool validate: If true, the object will be validated before creation%0A            :param bool clean: If true, optional fields that are null will be deleted%0A        '''
 %0A   
@@ -1530,24 +1530,25 @@
  schema%0A
+%0A
         
 attribut
@@ -1543,40 +1543,159 @@
     
-attributes = json_without_bytes(
+# Allows validation in python3%0A        self.attributes = json_without_bytes(copy.deepcopy(attributes))%0A        if clean:%0A            self.attributes = 
 stri
@@ -1702,16 +1702,21 @@
 p_empty(
+self.
 attribut
@@ -1711,32 +1711,41 @@
 elf.attributes, 
+required=
 self.schema.get(
@@ -1760,17 +1760,103 @@
 d', %5B%5D))
-)
+%0A        if validate:%0A            self.validate()%0A%0A    def validate(self, schema=None):
 %0A       
@@ -1876,16 +1876,21 @@
 alidate(
+self.
 attribut
@@ -1892,16 +1892,26 @@
 ributes,
+ schema or
  self.sc
@@ -1919,36 +1919,8 @@
 ema,
-%0A                           
  for
@@ -1935,73 +1935,28 @@
 ker=
-jsonschema.F
+self.f
 ormat
-C
+_c
 hecker
-())%0A%0A        self.attributes = attributes
+)
 %0A%0A  

clean up cleaning code, add flags to document creation allowing optional validation and cleaning