/* ====================================================================
 * Copyright (c) 2004-2006 Open Source Applications Foundation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions: 
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software. 
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * ====================================================================
 */

#include "converters.h"
#include <java/lang/Comparable.h>
#include <java/lang/Integer.h>
#include <java/lang/Long.h>
#include <java/lang/Float.h>
#include <java/lang/Double.h>
#include <java/util/Iterator.h>

#include "org/osafoundation/util/PythonComparable.h"
#include "macros.h"
#include "java.h"


jstring p2j(PyObject *object)
{
    if (object == Py_None)
        return NULL;
    else if (PyUnicode_Check(object))
    {
        if (sizeof(Py_UNICODE) == sizeof(jchar))
            return JvNewString((const jchar *) PyUnicode_AS_UNICODE(object),
                               PyUnicode_GET_SIZE(object));
        else
        {
            int len = PyUnicode_GET_SIZE(object);
            Py_UNICODE *pchars = PyUnicode_AS_UNICODE(object);
            jstring str = JvAllocString(len);
            jchar *jchars = JvGetStringChars(str);

            for (int i = 0; i < len; i++)
                jchars[i] = pchars[i];

            return str;
        }
    }
    else if (PyString_Check(object))
    {
        char *ps = PyString_AS_STRING(object);
        jstring js = JvNewStringUTF(ps);

        if (!js)
            PyErr_Format(PyExc_ValueError,
                         "python str is not unicode or utf-8 encoded: %s", ps);

        return js;
    }
    else
    {
        PyObject *tuple = Py_BuildValue("(sO)", "expected a string", object);

        PyErr_SetObject(PyExc_TypeError, tuple);
        Py_DECREF(tuple);

        return NULL;
    }
}

PyObject *j2p(jstring js)
{
    if (!js)
    {
        Py_INCREF(Py_None);
        return Py_None;
    }
    else if (sizeof(Py_UNICODE) == sizeof(jchar))
        return PyUnicode_FromUnicode((const Py_UNICODE *) JvGetStringChars(js),
                                     js->length());
    else
    {
        int len = js->length();
        PyObject *string = PyUnicode_FromUnicode(NULL, len);

        if (!string)
            return NULL;

        jchar *jchars = JvGetStringChars(js);
        Py_UNICODE *pchars = PyUnicode_AS_UNICODE(string);

        for (int i = 0; i < len; i++)
            pchars[i] = jchars[i];
        
        return string;
    }
}

java::lang::Comparable *pc2jc(PyObject *pc)
{
    if (PyInt_Check(pc))
    {
        long n = PyInt_AsLong(pc);
        return (java::lang::Comparable *) new ::java::lang::Integer(n);
    }

    if (PyLong_Check(pc))
    {
        long long n = PyLong_AsLongLong(pc);
        return (java::lang::Comparable *) new ::java::lang::Long(n);
    }
    
    if (PyFloat_Check(pc))
    {
        double n = PyFloat_AsDouble(pc);
        return (java::lang::Comparable *) new ::java::lang::Double((jdouble) n);
    }
    
    if (PyUnicode_Check(pc) || PyString_Check(pc))
        return (java::lang::Comparable *) p2j(pc);

    if (PyObject_TypeCheck(pc, &ComparableType))
        return (java::lang::Comparable *) ((j_object *) pc)->object;

    if (PyObject_HasAttrString(pc, "compareTo"))
    {
        jlong ptr;

        *(PyObject **) &ptr = (PyObject *) pc;
        return (java::lang::Comparable *) new ::org::osafoundation::util::PythonComparable(ptr);
    }
    
    PyErr_SetObject(PyExc_TypeError, pc);
    return NULL;
}

PyObject *jc2pc(java::lang::Comparable *jc)
{
    if (java::lang::Integer::class$.isInstance(jc))
        return PyInt_FromLong(((java::lang::Integer *) jc)->intValue());

    if (java::lang::Long::class$.isInstance(jc))
        return PyLong_FromLongLong((long long)((java::lang::Long *) jc)->longValue());

    if (java::lang::Float::class$.isInstance(jc))
        return PyFloat_FromDouble((double)((java::lang::Float *) jc)->doubleValue());

    if (java::lang::Double::class$.isInstance(jc))
        return PyFloat_FromDouble((double)((java::lang::Double *) jc)->doubleValue());

    if (java::lang::String::class$.isInstance(jc))
        return j2p((jstring) jc);

    if (org::osafoundation::util::PythonComparable::class$.isInstance(jc))
    {
        PyObject *pc = *(PyObject **) &(((org::osafoundation::util::PythonComparable *) jc)->pythonComparable);
        Py_INCREF(pc);

        return pc;
    }

    return wrap_Comparable(jc);
}

jstringArray psl2jsa(PyObject *psl)
{
    if (psl == Py_None)
        return NULL;

    if (!PySequence_Check(psl))
    {
        PyErr_SetString(PyExc_ValueError, "Expected a sequence");
        return NULL;
    }

    int len = PySequence_Length(psl);
    jstringArray array = (jstringArray)
        JvNewObjectArray(len, &::java::lang::String::class$, NULL);
    jstring *strings = elements(array);

    for (int i = 0; i < len; i++) {
        PyObject *obj = PySequence_GetItem(psl, i);

        strings[i] = p2j(obj);
        Py_DECREF(obj);

        if (!strings[i] && PyErr_Occurred())
            return NULL;
    }

    return array;
}

PyObject *jsa2psl(jstringArray jsa)
{
    if (jsa == NULL)
        Py_RETURN_NONE;

    int len = JvGetArrayLength(jsa);
    jstring *strings = elements(jsa);
    PyObject *psl = PyList_New(len);

    for (int i = 0; i < len; i++)
        PyList_SET_ITEM(psl, i, j2p(strings[i]));

    return psl;
}

PyObject *jsc2psl(java::util::Collection *jsc)
{
    if (jsc == NULL)
        Py_RETURN_NONE;

    int len = jsc->size();
    ::java::util::Iterator *iterator = jsc->iterator();
    PyObject *list = PyList_New(len);

    for (int i = 0; i < len; i++)
        PyList_SET_ITEM(list, i, j2p((jstring) iterator->next()));

    return list;
}

PyObject *jia2pil(jintArray jia)
{
    if (jia == NULL)
    {
        Py_INCREF(Py_None);
        return Py_None;
    }
    else
    {   
        int len = JvGetArrayLength(jia);
        jint *ints = elements(jia);
        PyObject *pil;

        pil = PyList_New(len);
        for (int i = 0; i < len; i++)
            PyList_SET_ITEM(pil, i, PyInt_FromLong(ints[i]));

        return pil;
    }
}

PyObject *jia2pil(jintArray jia, int count)
{
    if (jia == NULL)
    {
        Py_INCREF(Py_None);
        return Py_None;
    }
    else
    {   
        int len = JvGetArrayLength(jia);
        jint *ints = elements(jia);
        PyObject *pil;

        if (count >= 0 && count < len)
            len = count;

        pil = PyList_New(len);
        for (int i = 0; i < len; i++)
            PyList_SET_ITEM(pil, i, PyInt_FromLong(ints[i]));

        return pil;
    }
}

PyObject *jc2pl(java::util::Collection *jobjects, PyObject *(*wrapfn)(jobject))
{
    if (jobjects == NULL)
    {
        Py_INCREF(Py_None);
        return Py_None;
    }
    else
    {   
        int len = jobjects->size();
        java::util::Iterator *iterator = jobjects->iterator();
        PyObject *result = PyList_New(len);

        for (int i = 0; i < len; i++)
            PyList_SET_ITEM(result, i, wrapfn(iterator->next()));

        return result;
    }
}

PyObject *ja2pl(jobjectArray objects, PyObject *(*wrapfn)(jobject))
{
    if (objects == NULL)
        Py_RETURN_NONE;

    int len = JvGetArrayLength(objects);
    jobject *objs = elements(objects);
    PyObject *list = PyList_New(len);

    for (int i = 0; i < len; i++)
        PyList_SET_ITEM(list, i, wrapfn(objs[i]));

    return list;
}

jobjectArray pl2ja(PyObject *pl, jclass cls)
{
    if (pl == Py_None)
        return NULL;

    if (!PySequence_Check(pl))
    {
        PyErr_SetString(PyExc_ValueError, "Expected a sequence");
        return NULL;
    }

    int len = PySequence_Length(pl);
    jobjectArray array = JvNewObjectArray(len, cls, NULL);
    jobject *objects = elements(array);

    for (int i = 0; i < len; i++) {
        PyObject *obj = PySequence_GetItem(pl, i);
        jobject jo;

        if (obj == Py_None)
            jo = NULL;
        else if (PyObject_TypeCheck(obj, &ObjectType) &&
                 cls->isInstance(((j_object *) obj)->object))
            jo = ((j_object *) obj)->object;
        else
        {
            PyErr_SetObject(PyExc_TypeError, obj);
            return NULL;
        }

        objects[i] = jo;
        Py_DECREF(obj);
    }

    return array;
}

jobjectArray pl2ja(PyObject *pl, jclass cls,
                   int (*check)(PyObject *), jobject (*make)(PyObject *))
{
    if (pl == Py_None)
        return NULL;

    if (!PySequence_Check(pl))
    {
        PyErr_SetString(PyExc_ValueError, "Expected a sequence");
        return NULL;
    }

    int len = PySequence_Length(pl);
    jobjectArray array = JvNewObjectArray(len, cls, NULL);
    jobject *objects = elements(array);

    for (int i = 0; i < len; i++) {
        PyObject *obj = PySequence_GetItem(pl, i);
        jobject jo;

        if (obj == Py_None)
            jo = NULL;
        else if (PyObject_TypeCheck(obj, &ObjectType) &&
                 cls->isInstance(((j_object *) obj)->object))
            jo = ((j_object *) obj)->object;
        else if (check(obj))
            jo = make(obj);
        else
        {
            PyErr_SetObject(PyExc_TypeError, obj);
            return NULL;
        }

        objects[i] = jo;
        Py_DECREF(obj);
    }

    return array;
}

