#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2009-2011, Nicolas Clairon
# 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.
#     * Neither the name of the University of California, Berkeley nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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.

import bson
import datetime
import logging
from copy import deepcopy

log = logging.getLogger(__name__)

from mongokit.operators import SchemaOperator, IS
from mongokit.helpers import DotCollapsedDict
from mongokit.helpers import DotExpandedDict
from mongokit.helpers import i18nDotedDict
from mongokit.helpers import DotedDict

__all__ = [
    'AuthorizedTypeError',
    'BadKeyError',
    'CustomType',
    'DefaultFieldTypeError',
    'DotCollapsedDict',
    'DotedDict',
    'DotExpandedDict',
    'DuplicateDefaultValueError',
    'DuplicateRequiredError',
    'i18n',
    'i18nError',
    'ModifierOperatorError',
    'RequireFieldError',
    'SchemaDocument',
    'SchemaDocumentError',
    'SchemaProperties',
    'SchemaTypeError',
    'Set',
    'StructureError',
    'ValidationError',
]


class CustomType(object):
    init_type = None
    mongo_type = None
    python_type = None

    def __init__(self):
        if self.mongo_type is None:
            raise TypeError("`mongo_type` property must be specify in %s" %
                            self.__class__.__name__)
        if self.python_type is None:
            raise TypeError("`python_type` property must be specify in %s" %
                            self.__class__.__name__)

    def to_bson(self, value):
        """convert type to a mongodb type"""
        raise NotImplementedError

    def to_python(self, value):
        """convert type to a mongodb type"""
        raise NotImplementedError

    def validate(self, value, path):
        """
        This method is optional. It add a validation layer.
        This method is been called in Document.validate()

        value: the value of the field
        path: the field name (ie, 'foo' or 'foo.bar' if nested)
        """
        pass


# field wich does not need to be declared into the structure
STRUCTURE_KEYWORDS = []


class SchemaDocumentError(Exception):
    pass


class RequireFieldError(SchemaDocumentError):
    pass


class StructureError(SchemaDocumentError):
    pass


class BadKeyError(SchemaDocumentError):
    pass


class AuthorizedTypeError(SchemaDocumentError):
    pass


class ValidationError(SchemaDocumentError):
    pass


class DuplicateRequiredError(SchemaDocumentError):
    pass


class DuplicateDefaultValueError(SchemaDocumentError):
    pass


class ModifierOperatorError(SchemaDocumentError):
    pass


class SchemaTypeError(SchemaDocumentError):
    pass


class DefaultFieldTypeError(SchemaDocumentError):
    pass


class i18nError(SchemaDocumentError):
    pass


class DeprecationError(Exception):
    pass


class DuplicateI18nError(Exception):
    pass


class SchemaProperties(type):
    def __new__(mcs, name, bases, attrs):
        attrs['_protected_field_names'] = set(
            ['_protected_field_names', '_namespaces', '_required_namespace'])
        for base in bases:
            parent = base.__mro__[0]
            if not hasattr(parent, 'structure'):
                continue

            if parent.structure is not None:
                #parent = parent()
                if parent.structure:
                    if 'structure' not in attrs and parent.structure:
                        attrs['structure'] = parent.structure.copy()
                    else:
                        obj_structure = attrs.get('structure', {}).copy()
                        attrs['structure'] = parent.structure.copy()
                        attrs['structure'].update(obj_structure)