"""
.. module:: messages.codecs
   :synopsis: Encoding and decoding of specific datatypes.
"""

import datetime
import base64

from sqlalchemy import types
from dbsync import core
from dbsync.lang import *
from dbsync.utils import types_dict as bare_types_dict


def types_dict(class_):
    "Augments standard types_dict with model extensions."
    dict_ = bare_types_dict(class_)
    extensions = core.model_extensions.get(class_.__name__, {})
    for field, ext in extensions.iteritems():
        type_, _, _ = ext
        dict_[field] = type_
    return dict_


def _encode_table(type_):
    "*type_* is a SQLAlchemy data type."
    if isinstance(type_, types.Date):
        return lambda value: [value.year, value.month, value.day]
    elif isinstance(type_, types.DateTime):
        return lambda value: [value.year, value.month, value.day,
                              value.hour, value.minute, value.second,
                              value.microsecond]
    elif isinstance(type_, types.Time):
        return lambda value: [value.hour, value.minute, value.second,
                              value.microsecond]
    elif isinstance(type_, types.LargeBinary):
        return base64.standard_b64encode
    return identity

#: Encodes a python value into a JSON-friendly python value.
encode = lambda t: guard(_encode_table(t))

def encode_dict(class_):
    """
    Returns a function that transforms a dictionary, mapping the
    types to simpler ones, according to the given mapped class.
    """
    types = types_dict(class_)
    encodings = dict((k, encode(t)) for k, t in types.iteritems())
    return lambda dict_: dict((k, encodings[k](v))
                              for k, v in dict_.iteritems()
                              if k in encodings)


def _decode_table(type_):
    "*type_* is a SQLAlchemy data type."
    if isinstance(type_, types.Date):
        return lambda values: datetime.date(*values)
    elif isinstance(type_, types.DateTime):
        return lambda values: datetime.datetime(*values)
    elif isinstance(type_, types.Time):
        return lambda values: datetime.time(*values)
    elif isinstance(type_, types.LargeBinary):
        return base64.standard_b64decode
    return identity

#: Decodes a value coming from a JSON string into a richer python value.
decode = lambda t: guard(_decode_table(t))

def decode_dict(class_):
    """
    Returns a function that transforms a dictionary, mapping the
    types to richer ones, according to the given mapped class.
    """
    types = types_dict(class_)
    decodings = dict((k, decode(t)) for k, t in types.iteritems())
    return lambda dict_: dict((k, decodings[k](v))
                              for k, v in dict_.iteritems()
                              if k in decodings)

@@ -105,16 +105,94 @@
 atetime%0A
+import time # TODO remove time import after the deprecated codecs are removed%0A
 import b
@@ -740,32 +740,69 @@
 s.Date):%0A       
+ return method('toordinal')%0A        #
  return lambda v
@@ -908,32 +908,94 @@
 rn lambda value:
+ time.mktime(value.timetuple())%0A        # return lambda value:
  %5Bvalue.year, va
@@ -1015,32 +1015,34 @@
 lue.day,%0A       
+ #
                 
@@ -1087,32 +1087,34 @@
 .second,%0A       
+ #
                 
@@ -1942,16 +1942,424 @@
 ings)%0A%0A%0A
+def decode_date(value):%0A    %22TODO remove deprecated, backwards-compatible decoder%22%0A    if isinstance(value, list):%0A        return datetime.date(*value)%0A    return datetime.date.fromordinal(value)%0A%0Adef decode_datetime(value):%0A    %22TODO remove deprecated, backwards-compatible decoder%22%0A    if isinstance(value, list):%0A        return datetime.datetime(*value)%0A    return datetime.datetime.fromtimestamp(value)%0A%0A
 def _dec
@@ -2454,32 +2454,61 @@
 s.Date):%0A       
+ return decode_date%0A        #
  return lambda v
@@ -2580,32 +2580,65 @@
 teTime):%0A       
+ return decode_datetime%0A        #
  return lambda v

Revert to backwards compatible codecs.