# This program is free software; you can redistribute it and/or modify
# it under the terms of the (LGPL) GNU Lesser General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library Lesser General Public License for more details at
# ( http://www.gnu.org/licenses/lgpl.html ).
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# written by: Jeff Ortel ( jortel@redhat.com )

"""
The I{sxbuiltin} module provides classes that represent
XSD I{builtin} schema objects.
"""

from suds.sax.date import Date, Time, DateTime
from suds.xsd.sxbase import XBuiltin

import datetime as dt


class XString(XBuiltin):
    """
    Represents an (xsd) <xs:string/> node
    """

    pass


class XAny(XBuiltin):
    """
    Represents an (xsd) <any/> node
    """

    def __init__(self, schema, name):
        XBuiltin.__init__(self, schema, name)
        self.nillable = False

    def get_child(self, name):
        child = XAny(self.schema, name)
        return child, []

    def any(self):
        return True


class XBoolean(XBuiltin):
    """
    Represents an (xsd) boolean builtin type.
    """

    translation = (
        {"1": True, "true": True, "0": False, "false": False},
        {True: "true", 1: "true", False: "false", 0: "false"},
    )

    @staticmethod
    def translate(value, topython=True):
        if topython:
            if isinstance(value, str):
                return XBoolean.translation[0].get(value)
        else:
            if isinstance(value, (bool, int)):
                return XBoolean.translation[1].get(value)
            return value


class XInteger(XBuiltin):
    """
    Represents an (xsd) xs:int builtin type.
    """

    @staticmethod
    def translate(value, topython=True):
        if topython:
            if isinstance(value, str) and len(value):
                return int(value)
        else:
            if isinstance(value, int):
                return str(value)
            return value


class XLong(XBuiltin):
    """
    Represents an (xsd) xs:long builtin type.
    """

    @staticmethod
    def translate(value, topython=True):
        if topython:
            if isinstance(value, str) and len(value):
                return int(value)
        else:
            if isinstance(value, int):
                return str(value)
            return value


class XFloat(XBuiltin):
    """
    Represents an (xsd) xs:float builtin type.
    """

    @staticmethod
    def translate(value, topython=True):
        if topython:
            if isinstance(value, str) and len(value):
                return float(value)
        else:
            if isinstance(value, float):
                return str(value)
            return value


class XDate(XBuiltin):
    """
    Represents an (xsd) xs:date builtin type.
    """

    @staticmethod
    def translate(value, topython=True):
        if topython:
            if isinstance(value, str) and len(value):
                return Date(value).value
        else:
            if isinstance(value, dt.date):
                return str(Date(value))
            return value


class XTime(XBuiltin):
    """
    Represents an (xsd) xs:time builtin type.
    """

    @staticmethod
    def translate(value, topython=True):
        if topython:
            if isinstance(value, str) and len(value):
                return Time(value).value
        else:
            if isinstance(value, dt.time):
                return str(Time(value))
            return value


class XDateTime(XBuiltin):
    """
    Represents an (xsd) xs:datetime builtin type.
    """

    @staticmethod
    def translate(value, topython=True):
        if topython:
            if isinstance(value, str) and len(value):
                return DateTime(value).value
        else:
            if isinstance(value, dt.datetime):
                return str(DateTime(value))
            return value


class Factory:

    tags = {
        # any
        "anyType": XAny,
        # strings
        "string": XString,
        "normalizedString": XString,
        "ID": XString,
        "Name": XString,
        "QName": XString,
        "NCName": XString,
        "anySimpleType": XString,
        "anyURI": XString,
        "NOTATION": XString,
        "token": XString,
        "language": XString,
        "IDREFS": XString,
        "ENTITIES": XString,
        "IDREF": XString,
        "ENTITY": XString,
        "NMTOKEN": XString,
        "NMTOKENS": XString,
        # binary
        "hexBinary": XString,
        "base64Binary": XString,
        # integers
        "int": XInteger,
        "integer": XInteger,
        "unsignedInt": XInteger,
        "positiveInteger": XInteger,
        "negativeInteger": XInteger,
        "nonPositiveInteger": XInteger,
        "nonNegativeInteger": XInteger,
        # longs
        "long": XLong,
        "unsignedLong": XLong,
        # shorts
        "short": XInteger,
        "unsignedShort": XInteger,
        "byte": XInteger,
        "unsignedByte": XInteger,
        # floats
        "float": XFloat,
        "double": XFloat,
        "decimal": XFloat,
        # dates & times
        "date": XDate,
        "time": XTime,
        "dateTime": XDateTime,
        "duration": XString,
        "gYearMonth": XString,
        "gYear": XString,
        "gMonthDay": XString,
        "gDay": XString,
        "gMonth": XString,
        # boolean
        "boolean": XBoolean,
    }

    @classmethod
    def maptag(cls, tag, fn):
        """
        Map (override) tag => I{class} mapping.
        @param tag: An xsd tag name.
        @type tag: str
        @param fn: A function or class.
        @type fn: fn|class.
        """
        cls.tags[tag] = fn

    @classmethod
    def create(cls, schema, name):
        """
        Create an object based on the root tag name.
        @param schema: A schema object.
        @type schema: L{schema.Schema}
        @param name: The name.
        @type name: str
        @return: The created object.
        @rtype: L{XBuiltin}
        """
        fn = cls.tags.get(name)
        if fn is not None:
            return fn(schema, name)
        return XBuiltin(schema, name)
