import pytz
import datetime
from inflector import Inflector

from collections import OrderedDict

from rethinkengine.connection import get_conn
from rethinkengine.fields import BaseField, ObjectIdField, ReferenceField
from rethinkengine.query_set import QuerySetManager
from rethinkengine.errors import DoesNotExist, \
    MultipleObjectsReturned, RqlOperationError, ValidationError

import inspect
import rethinkdb as r

__all__ = ['BaseDocument', 'Document']

class BaseDocument(type):
    def __new__(mcs, name, bases, attrs):
        new_class = super(BaseDocument, mcs).__new__(mcs, name, bases, attrs)

        # If new_class is of type Document, return straight away
        if object in new_class.__bases__:
            return new_class

        # Process schema
        fields = sorted(
            inspect.getmembers(
                new_class,
                lambda o: isinstance(o, BaseField)
            ),
            key=lambda i: i[1]._creation_order)
        new_class._fields = attrs.get('_fields', OrderedDict())
        new_class._fields['id'] = ObjectIdField()
        for field_name, field in fields:
            new_class._fields[field_name] = field
            delattr(new_class, field_name)
        new_class.objects = QuerySetManager()

        # Merge exceptions
        classes_to_merge = (DoesNotExist, MultipleObjectsReturned)
        for c in classes_to_merge:
            exc = type(c.__name__, (c,), {'__module__': name})
            setattr(new_class, c.__name__, exc)

        # Populate table_name if not privided
        if new_class.__table_name__ is None:
            new_class.__table_name__ = Inflector().pluralize(name).lower()
        return new_class


class Document(object):
    __metaclass__ = BaseDocument
    
    __table_name__ = None
    __primary_key__ = 'id'
    __order_by__ = None

    def __init__(self, **kwargs):
        super(Document, self).__init__()
        self.__dict__['_data'] = {}
        self.__dict__['_iter'] = None
        self.__dict__['_dirty'] = True
        for name, value in kwargs.items():
            setattr(self, name, value)

    def __setattr__(self, key, value):
        field = self._fields.get(key, None)
        if field is not None:
            #Fix timezone for datetime
            if isinstance(value, datetime.datetime) and not value.tzinfo:
                value = pytz.utc.localize(value)
            if self._get_value(key) != value:
                self._dirty = True
            #Add _id if field if ReferenceField
            if isinstance(self._fields.get(key), ReferenceField):
                key += '_id'
            self._data[key] = value
        super(Document, self).__setattr__(key, value)

    def __getattr__(self, key):
        field = self._fields.get(key)
        if field:
            return self._get_value(key)
        raise AttributeError

    def __str__(self):
        return '<%s object>' % self.__class__.__name__

    def __iter__(self):
        return self