import rpython.translator.jvm.typesystem as jvm
from rpython.rtyper.ootypesystem import ootype
from rpython.translator.jvm.typesystem import \
     jInt, jVoid, jStringBuilder, jString, jPyPy, jChar, jArrayList, jObject, \
     jBool, jHashMap, jPyPyDictItemsIterator, Generifier, jCharSequence, \
     jPyPyCustomDict

# ______________________________________________________________________
# Mapping of built-in OOTypes to JVM types

class JvmBuiltInType(jvm.JvmClassType):
    
    """
    Represents built-in types to JVM.  May optionally be associated
    with an OOTYPE; if it is, then we will support lookup of the OOTYPE
    methods and will re-map them as needed to the JVM equivalents.
    """
    
    def __init__(self, db, classty, OOTYPE):
        jvm.JvmClassType.__init__(self, classty.name)
        self.db = db
        self.OOTYPE = OOTYPE
        self.gen = Generifier(OOTYPE)
    
    def __eq__(self, other):
        return isinstance(other, JvmBuiltInType) and other.name == self.name

    def __hash__(self):
        return hash(self.name)

    def lookup_field(self, fieldnm):
        """ Given a field name, returns a jvm.Field object """
        _, FIELDTY = self.OOTYPE._lookup_field(fieldnm)
        jfieldty = self.db.lltype_to_cts(FIELDTY)
        return jvm.Field(
            self.descriptor.class_name(), fieldnm, jfieldty, False)

    def lookup_method(self, methodnm):
        """ Given the method name, returns a jvm.Method object """

        # Look for a shortcut method in our table of remappings:
        try:
            key = (self.OOTYPE.__class__, methodnm)
            return built_in_methods[key]
        except KeyError: pass

        # Otherwise, determine the Method object automagically
        #   First, map the OOTYPE arguments and results to
        #   the java types they will be at runtime.  Note that
        #   we must use the erased types for this.
        ARGS, RESULT = self.gen.erased_types(methodnm)
        jargtypes = [self.db.lltype_to_cts(P) for P in ARGS]
        jrettype = self.db.lltype_to_cts(RESULT)
        
        if self.OOTYPE.__class__ in bridged_objects:
            # Bridged objects are ones where we have written a java class
            # that has methods with the correct names and types already
            return jvm.Method.v(self, methodnm, jargtypes, jrettype)
        else:
            # By default, we assume it is a static method on the PyPy
            # object, that takes an instance of this object as the first
            # argument.  The other arguments we just convert to java versions,
            # except for generics.
            jargtypes = [self] + jargtypes
            return jvm.Method.s(jPyPy, methodnm, jargtypes, jrettype)

# When we lookup a method on a BuiltInClassNode, we first check the
# 'built_in_methods' and 'bridged_objects' tables.  This allows us to
# redirect to other methods if we like.

bridged_objects = (
    ootype.DictItemsIterator,
    ootype.WeakReference.__class__
    )

built_in_methods = {

    # Note: String and StringBuilder are rebound in ootype, and thus
    # .__class__ is required
    
    (ootype.StringBuilder.__class__, "ll_allocate"):
    jvm.Method.v(jStringBuilder, "ensureCapacity", (jInt,), jVoid),
    
    (ootype.StringBuilder.__class__, "ll_build"):
    jvm.Method.v(jStringBuilder, "toString", (), jString),

    (ootype.StringBuilder.__class__, "ll_getlength"):
    jvm.Method.v(jStringBuilder, "length", (), jInt),

    (ootype.String.__class__, "ll_hash"):
    jvm.Method.v(jString, "hashCode", (), jInt),

    (ootype.String.__class__, "ll_streq"):
    jvm.Method.v(jString, "equals", (jObject,), jBool),

    (ootype.String.__class__, "ll_strlen"):
    jvm.Method.v(jString, "length", (), jInt),
    
    (ootype.String.__class__, "ll_stritem_nonneg"):
    jvm.Method.v(jString, "charAt", (jInt,), jChar),

    (ootype.String.__class__, "ll_startswith"):
    jvm.Method.v(jString, "startsWith", (jString,), jBool),

    (ootype.String.__class__, "ll_endswith"):
    jvm.Method.v(jString, "endsWith", (jString,), jBool),

    (ootype.String.__class__, "ll_strcmp"):
    jvm.Method.v(jString, "compareTo", (jString,), jInt),

    (ootype.String.__class__, "ll_upper"):
    jvm.Method.v(jString, "toUpperCase", (), jString),

    (ootype.String.__class__, "ll_lower"):
    jvm.Method.v(jString, "toLowerCase", (), jString),

    (ootype.String.__class__, "ll_replace_chr_chr"):
    jvm.Method.v(jString, "replace", (jChar, jChar), jString),

    (ootype.Dict, "ll_set"):
    jvm.Method.v(jHashMap, "put", (jObject, jObject), jObject),
    
    (ootype.Dict, "ll_get"):
    jvm.Method.v(jHashMap, "get", (jObject,), jObject),

    (ootype.Dict, "ll_contains"):
    jvm.Method.v(jHashMap, "containsKey", (jObject,), jBool),

    (ootype.Dict, "ll_length"):
    jvm.Method.v(jHashMap, "size", (), jInt),
    
    (ootype.Dict, "ll_clear"):
    jvm.Method.v(jHashMap, "clear", (), jVoid),

    (ootype.CustomDict, "ll_set"):
    jvm.Method.v(jPyPyCustomDict, "put", (jObject, jObject), jObject),
    
    (ootype.CustomDict, "ll_get"):
    jvm.Method.v(jPyPyCustomDict, "get", (jObject,), jObject),

    (ootype.CustomDict, "ll_contains"):
    jvm.Method.v(jPyPyCustomDict, "containsKey", (jObject,), jBool),

    (ootype.CustomDict, "ll_length"):
    jvm.Method.v(jPyPyCustomDict, "size", (), jInt),
    
    (ootype.CustomDict, "ll_clear"):
    jvm.Method.v(jPyPyCustomDict, "clear", (), jVoid),

    (ootype.List, "ll_length"):
    jvm.Method.v(jArrayList, "size", (), jInt),

    (ootype.List, "ll_getitem_fast"):
    jvm.Method.v(jArrayList, "get", (jInt,), jObject),

    }

# ootype.String[Builder] and ootype.Unicode[Builder] are mapped to the
# same JVM type, so we reuse the same builtin methods also for them
def add_unicode_methods():
    mapping = {
        ootype.String.__class__: ootype.Unicode.__class__,
        ootype.StringBuilder.__class__: ootype.UnicodeBuilder.__class__
        }
    
    for (TYPE, name), value in built_in_methods.items():
        if TYPE in mapping:
            TYPE = mapping[TYPE]
            built_in_methods[TYPE, name] = value
add_unicode_methods()
del add_unicode_methods
