#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

import os, sys, platform, shutil, _jcc2
from itertools import izip

from cpp import PRIMITIVES, INDENT, HALF_INDENT
from cpp import RENAME_METHOD_SUFFIX, RENAME_FIELD_SUFFIX
from cpp import cppname, cppnames, absname, typename, findClass
from cpp import line, signature, find_method, split_pkg, sort
from cpp import Modifier, Class, Method
from cpp import getActualTypeArguments, getTypeParameters
from config import INCLUDES, CFLAGS, DEBUG_CFLAGS, LFLAGS, IMPLIB_LFLAGS, \
    SHARED, VERSION as JCC_VER

try:
    from cpp import ParameterizedType, TypeVariable
except ImportError:
    pass

python_ver = '%d.%d.%d' %(sys.version_info[0:3])
if python_ver < '2.4':
    from sets import Set as set


RESULTS = { 'boolean': 'Py_RETURN_BOOL(%s);',
            'byte': 'return PyInt_FromLong((long) %s);',
            'char': 'return PyUnicode_FromUnicode((Py_UNICODE *) &%s, 1);',
            'double': 'return PyFloat_FromDouble((double) %s);',
            'float': 'return PyFloat_FromDouble((double) %s);',
            'int': 'return PyInt_FromLong((long) %s);',
            'long': 'return PyLong_FromLongLong((PY_LONG_LONG) %s);',
            'short': 'return PyInt_FromLong((long) %s);',
            'ja
va.lang.String': 'return j2p(%s);' }

CALLARGS = { 'boolean': ('O', '(%s ? Py_True : Py_False)', False),
             'byte': ('O', 'PyInt_FromLong(%s)', True),
             'char': ('O', 'PyUnicode_FromUnicode((Py_UNICODE *) &%s, 1)', True),
             'double': ('d', '(double) %s', False),
             'float': ('f', '(float) %s', False),
             'int': ('i', '(int) %s', 
False),
             'long': ('L', '(long long) %s', False),
             'short': ('i', '(int) %s', False),
             'java.lang.String': ('O', 'env->fromJString((jstring) %s, 0)', True) }

BOXED = { 'java.lang.Boolean': (True, True),
          'java.lang.Byte': (True, True),
          'java.lang.Character': (True, True),
          'java.lang.CharSequence': (True, False),
          'java.lang.Double': (True, True),
          'java.lang.Float': (True, True),
          'java.lang.Integer': (True, True),
          'java.lang.Long': (True, True),
          'java.lang.Number': (True, False),
          'java.lang.Short': (True, True),
          'java.lang.String': (True, True) }


def is_boxed(clsName):
    return BOXED.get(clsName, (False, False))[0]

def is_unboxed(clsName):
    return BOXED.get(clsName, (False, False))[1]


def parseArgs(params, current, generics, genericParams=None):

    def signature(cls, genericPT=None):
        if generics and TypeVariable.instance_(genericPT):
            if cls.getName() == 'java.lang.Object':
                gd = TypeVariable.cast_(genericPT).getGenericDeclaration()
                if gd == current:
                    for clsParam in getTypeParameters(gd):
                        if genericPT == clsParam:
                            return 'O'
        array = ''
        while cls.isArray():
            array += '['
            cls = cls.getComponentType()
        clsName = cls.getName()
        if cls.isPrimitive():
            return array + PRIMITIVES[clsName]
        if clsName == 'java.lang.String':
            return array + 's'
        if clsName == 'java.lang.Object':
            return array + 'o'
        if is_boxed(clsName):
            return array + 'O'
        if generics and getTypeParameters(cls):
            return array + 'K'
        else:
            return array + 'k'

    def checkarg(cls, genericPT=None):
        if generics and TypeVariable.instance_(genericPT):
            if cls.getName() == 'java.lang.Object':
                gd = TypeVariable.cast_(genericPT).getGenericDeclaration()
                if gd == current:
                    i = 0
                    for clsParam in getTypeParameters(gd):
                        if genericPT == clsParam:
                            return ', self->parameters[%d]' %(i)
                        i += 1
        while cls.isArray():
            cls = cls.getComponentType()
        clsName = cls.getName()
        if (cls.isPrimitive() or
            clsName in ('java.lang.String', 'java.lang.Object')):
            return ''
        if is_boxed(clsName):
            clsNames = clsName.split('.')
            return ', &%s::PY_TYPE(%s)' %(absname(cppnames(clsNames[:-1])), cppname(clsNames[-1]))
        return ', %s::initializeClass' %(typename(cls, current, False))

    def callarg(cls, i):
        if generics:
            while cls.isArray():
                cls = cls.getComponentType()
            if getTypeParameters(cls):
                ns, sep, n = rpartition(typename(cls, current, False), '::')
                return ', &a%d, &p%d, %s%st_%s::parameters_' %(i, i, ns, sep, n)
        return ', &a%d' %(i)

    if genericParams:
        sig = ''.join([signature(param, genericParam)
                       for param, genericParam in izip(params, genericParams)])
        chk = ''.join([checkarg(param, genericParam)
                       for param, genericParam in izip(params, genericParams)])
    else:
        sig = ''.join([signature(param) for param in params])
        chk = ''.join([checkarg(param) for param in params])

    return (sig, chk,
            ''.join([callarg(params[i], i) for i in xrange(len(params))]))


def declareVars(out, indent, params, current, generics, typeParams):

    for i in xrange(len(params)):
        param = params[i]
        line(out, indent, '%s a%d%s;',
             typename(param, current, False), i,
             not param.isPrimitive() and '((jobject) NULL)' or '')
        if generics:
            while param.isArray():
                param = param.getComponentType()
            if getTypeParameters(param):
                line(out, indent, 'PyTypeObject **p%d;', i)
                typeParams.add(i)
    

def construct(out, indent, cls, inCase, constructor, names, generics):

    if inCase:
        line(out, indent, '{')
        indent += 1

    params = constructor.getParameterTypes()
    if generics:
        typeParams = set()
    else:
        typeParams = None

    count = len(params)

    declareVars(out, indent, params, cls, generics, typeParams)
    line(out, indent, '%s object((jobject) NULL);', cppname(names[-1]))

    line(out)
    if count:
        line(out, indent, 'if (!parseArgs(args, "%s"%s%s))',
             *parseArgs(params, cls, generics))
        line(out, indent, '{')
        indent += 1

    line(out, indent, 'INT_CALL(object = %s(%s));',
         cppname(names[-1]), ', '.join(['a%d' %(i) for i in xrange(count)]))
    line(out, indent, 'self->object = object;')

    if generics:
        clsParams = getTypeParameters(cls)
        i = 0
        for clsParam in clsParams:
            if Class.instance_(clsParam):
                cls = Class.cast_(clsParam)
                if cls.isArray():
                    cls = cls.getComponentType()
                    if cls.isArray():
                        clsNames = 'java.lang.Object'.split('.')
                        clsArg = '&%s::PY_TYPE(%s)' %(absname(cppnames(clsNames[:-1])), cppname(clsNames[-1]))
                    elif cls.isPrimitive():
                        clsArg = 'PY_TYPE(JArray%s)' %(cls.getName().capitalize())
                    else:
                        clsArg = 'PY_TYPE(JArrayObject)'
                else:
                    clsNames = cls.getName().split('.')
                    clsArg = '&%s::PY_TYPE(%s)' %(absname(cppnames(clsNames[:-1])), cppname(clsNames[-1]))
                line(out, indent, 'self->parameters[%d] = %s;', i, clsArg)
            i += 1
    
    if inCase:
     