# KInterbasDB Python Package - Type Conv : Fixed/Standard Library
#
# Version 3.1
#
# The following contributors hold Copyright (C) over their respective
# portions of code (see license.txt for details):
#
# [Original Author (maintained through version 2.0-0.3.1):]
#   1998-2001 [alex]  Alexander Kuznetsov   <alexan@users.sourceforge.net>
# [Maintainers (after version 2.0-0.3.1):]
#   2001-2002 [maz]   Marek Isalski         <kinterbasdb@maz.nu>
#   2002-2004 [dsr]   David Rushby          <woodsplitter@rocketmail.com>
# [Contributors:]
#   2001      [eac]   Evgeny A. Cherkashin  <eugeneai@icc.ru>
#   2001-2002 [janez] Janez Jere            <janez.jere@void.si>

__all__ = (
    # kinterbasdb-native fixed point converters (old, precison_mode style):
    'fixed_conv_in_imprecise', 'fixed_conv_in_precise',
    'fixed_conv_out_imprecise', 'fixed_conv_out_precise',
  )

import sys, types
from kinterbasdb.k_exceptions import *

if sys.version_info < (2,2):
    from kinterbasdb.typeconv_util_isinstance import isinstance

################################################################################
## FIXED POINT
################################################################################

# With versions of Python that have at least rudimentary int/long unification
# (2.2 and later), use ints where possible:
if sys.version_info < (2,2):
    ten = 10L
else:
    ten = 10
_tenTo = [ten**x for x in range(20)]
del x, ten

# The fixed point input funcions are GUARANTEED to receive a single parameter
# that is a 2-tuple of the form: (original input object, scale).
# The original input object may, of course, be None.
def fixed_conv_in_imprecise((val, scale)):
    if val is None or isinstance(val, types.StringType): # Allow implicit param conv.
        return val
    if not isinstance(val, (types.FloatType, types.IntType, types.LongType)):
        raise InterfaceError(
            'float required as input for fixed point field in imprecise mode.'
          )
    absScale = abs(scale)
    return long(round(val, absScale) * _tenTo[absScale])

def fixed_conv_in_precise((val, scale)):
    if val is None or isinstance(val, types.StringType): # Allow implicit param conv.
        return val
    if not isinstance(val, (types.IntType, types.LongType)):
        raise InterfaceError(
            'int or long required as input for fixed point field in precise mode.'
          )
    # $val is already a scaled integer; just pass it through.
    return val


# The fixed point output funcions receive a single parameter that is either
# - None (if the SQL value is NULL), or
# - a 2-tuple of the form: (scaled integer value, scale)
def fixed_conv_out_imprecise(x):
    # Return a floating point representation of the scaled integer.
    if x is None:
        return None
    (val, scale) = x
    if scale == 0:
        return val # Don't convert to float if no decimal places.
    return float(val) / _tenTo[abs(scale)]

def fixed_conv_out_precise(x):
    # Simply return the scaled integer, not interpreted in any way.
    if x is None:
        return None
    return x[0]
