"""
A symbol table object to hold types for the parser.
"""

from __future__ import absolute_import, division, print_function

__all__ = ['TypeSymbolTable', 'sym']

import ctypes

from . import coretypes as ct

_is_64bit = (ctypes.sizeof(ctypes.c_void_p) == 8)

def _complex(tp):
    """Simple temporary type constructor for complex"""
    if tp == ct.DataShape(ct.float32):
        return ct.complex_float32
    elif tp == ct.DataShape(ct.float64):
        return ct.complex_float64
    else:
        raise TypeError('Cannot contruct a complex type with real component %s' % tp)

def _struct(names, dshapes):
    """Simple temporary type constructor for struct"""
    return ct.Record(list(zip(names, dshapes)))

def _funcproto(args, ret):
    """Simple temporary type constructor for funcproto"""
    return ct.Function(*(args + [ret]))

def _typevar_dim(name):
    """Simple temporary type constructor for typevar as a dim"""
    # Note: Presently no difference between dim and dtype typevar
    return ct.TypeVar(name)

def _typevar_dtype(name):
    """Simple temporary type constructor for typevar as a dtype"""
    # Note: Presently no difference between dim and dtype typevar
    return ct.TypeVar(name)

def _ellipsis(name):
    return ct.Ellipsis(ct.TypeVar(name))

# data types with no type constructor
no_constructor_types = \
    [('bool', ct.bool_),
     ('int8', ct.int8),
     ('int16', ct.int16),
     ('int32', ct.int32),
     ('int64', ct.int64),
     ('intptr', ct.int64 if _is_64bit else ct.int32),
     ('int', ct.int32),
     ('uint8', ct.uint8),
     ('uint16', ct.uint16),
     ('uint32', ct.uint32),
     ('uint64', ct.uint64),
     ('uintptr', ct.uint64 if _is_64bit else ct.uint32),
     ('float32', ct.float32),
     ('float64', ct.float64),
     ('complex64', ct.complex64),
     ('complex128', ct.complex128),
     ('real', ct.float64),
     ('complex', ct.complex_float64),
     ('string', ct.string),
     ('json', ct.json),
     ('date', ct.date_),
     ('time', ct.time_),
     ('datetime', ct.datetime_)]

# data types with a type constructor
constructor_types = \
    [('complex', _complex),
     ('string', ct.String),
     ('struct', _struct),
     ('tuple', ct.Tuple),
     ('funcproto', _funcproto),
     ('typevar', _typevar_dtype),
     ('option', ct.Option),
     ('time', ct.Time),
     ('datetime', ct.DateTime),
     ('units', ct.Units)]

# dim types with no type constructor
dim_no_constructor = \
    [('var', ct.Var()),
     ('ellipsis', ct.Ellipsis())]

# dim types with a type constructor
dim_constructor = \
    [('fixed', ct.Fixed),
     ('typevar', _typevar_dim),
     ('ellipsis', _ellipsis)]

class TypeSymbolTable(object):
    """
    This is a class which holds symbols for types and type constructors,
    and is used by the datashape parser to build types during its parsing.
    A TypeSymbolTable sym has four tables, as follows:

    sym.dtype
        Data type symbols with no type constructor.
    sym.dtype_constr
        Data type symbols with a type constructor. This may contain
        symbols also in sym.dtype, e.g. for 'complex' and 'complex[float64]'.
    sym.dim
        Dimension symbols with no type constructor.
    sym.dim_constr
        Dimension symbols with a type constructor.
    """
    __slots__ = ['dtype', 'dtype_constr', 'dim', 'dim_constr']

    def __init__(self, bare=False):
        # Initialize all the symbol tables to empty dicts1
        self.dtype = {}
        self.dtype_constr = {}
        self.dim = {}
        self.dim_constr = {}
        if not bare:
            self.add_default_types()

    def add_default_types(self):
        """
        Adds all the default datashape types to the symbol table.
        """
        self.dtype.update(no_constructor_types)
        self.dtype_constr.update(constructor_types)
        self.dim.update(dim_no_constructor)
        self.dim_constr.update(dim_constructor)

# Create the default global type symbol table
sym = TypeSymbolTable()
