/* ====================================================================
 * Copyright (c) 2004-2005 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 "PyLucene.h"
#include <java/util/BitSet.h>

#include "org/osafoundation/search/PythonSortComparatorSource.h"
#include "org/osafoundation/search/PythonSortComparator.h"
#include "org/osafoundation/search/PythonScoreDocComparator.h"
#include "org/osafoundation/search/PythonFilter.h"
#include "org/osafoundation/search/PythonHitCollector.h"
#include "org/osafoundation/search/PythonSimilarity.h"
#include "org/osafoundation/search/PythonFilteredTermEnum.h"
#include "org/osafoundation/search/PythonSearcher.h"
#include "org/osafoundation/util/PythonException.h"
#include "org/apache/lucene/document/Document.h"
#include "org/apache/lucene/search/ScoreDocComparator.h"
#include "org/apache/lucene/search/ScoreDoc.h"
#include "org/apache/lucene/search/Searcher.h"
#include "org/apache/lucene/search/Query.h"
#include "org/apache/lucene/search/Weight.h"
#include "org/apache/lucene/search/Explanation.h"
#include "org/apache/lucene/search/Filter.h"
#include "org/apache/lucene/search/TopDocs.h"
#include "org/apache/lucene/search/TopFieldDocs.h"
#include "org/apache/lucene/search/Sort.h"
#include "org/apache/lucene/search/HitCollector.h"
#include "org/apache/lucene/index/IndexReader.h"
#include "org/apache/lucene/index/TermEnum.h"
#include "org/apache/lucene/index/Term.h"


/**
 * The native functions declared in: 
 *     org.osafoundation.search.PythonSortComparatorSource
 *     org.osafoundation.search.PythonSortComparator
 *     org.osafoundation.search.PythonScoreDocComparator
 *     org.osafoundation.search.PythonFilter
 *     org.osafoundation.search.PythonHitCollector
 *     org.osafoundation.search.PythonSimilarity
 *     org.osafoundation.search.PythonFilteredTermEnum
 *     org.osafoundation.search.PythonSearcher
 * 
 * @author Andi Vajda
 */

namespace org {
    namespace osafoundation {
        namespace search {

            // org.osafoundation.search.PythonSortComparatorSource

            void PythonSortComparatorSource::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonSortComparatorSource);
            }

            void PythonSortComparatorSource::decRef(void)
            {
                finalizeObject(pythonSortComparatorSource);
            }

            ::org::apache::lucene::search::ScoreDocComparator *PythonSortComparatorSource::newComparator(::org::apache::lucene::index::IndexReader *reader, jstring fieldName)
            {
                PythonGIL gil;
                PyObject *pyReader = wrap_IndexReader(reader);
                PyObject *pyFieldName = j2p(fieldName);
                PyObject *pysdc = callPython(*(PyObject **) &pythonSortComparatorSource, "newComparator", pyReader, pyFieldName, NULL);

                Py_DECREF(pyReader);
                Py_DECREF(pyFieldName);

                if (!pysdc)
                    throw new org::osafoundation::util::PythonException();

                jlong ptr;
                *(PyObject **) &ptr = pysdc;
                jobject jsdc = new PythonScoreDocComparator(ptr);

                Py_DECREF(pysdc);
                return (::org::apache::lucene::search::ScoreDocComparator *) jsdc;
            }

            // org.osafoundation.search.PythonSortComparator

            void PythonSortComparator::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonSortComparator);
            }

            void PythonSortComparator::decRef(void)
            {
                finalizeObject(pythonSortComparator);
            }

            ::org::apache::lucene::search::ScoreDocComparator *PythonSortComparator::newComparator(::org::apache::lucene::index::IndexReader *reader, jstring fieldName)
            {
                PythonGIL gil;
                PyObject *pyReader = wrap_IndexReader(reader);
                PyObject *pyFieldName = j2p(fieldName);
                PyObject *pysdc = callPython(*(PyObject **) &pythonSortComparator, "newComparator", pyReader, pyFieldName, NULL);

                Py_DECREF(pyReader);
                Py_DECREF(pyFieldName);

                if (!pysdc)
                    throw new org::osafoundation::util::PythonException();

                jlong ptr;
                *(PyObject **) &ptr = pysdc;
                jobject jsdc = new PythonScoreDocComparator(ptr);

                Py_DECREF(pysdc);
                return (::org::apache::lucene::search::ScoreDocComparator *) jsdc;
            }

            ::java::lang::Comparable *PythonSortComparator::getComparable(jstring termText)
            {
                PythonGIL gil;
                PyObject *pyTermText = j2p(termText);
                PyObject *pyc = callPython(*(PyObject **) &pythonSortComparator, "getComparable", pyTermText, NULL);

                Py_DECREF(pyTermText);

                if (!pyc)
                    throw new org::osafoundation::util::PythonException();

                ::java::lang::Comparable *jc = pc2jc(pyc);
                Py_DECREF(pyc);

                return jc;
            }

            // org.osafoundation.search.PythonScoreDocComparator

            void PythonScoreDocComparator::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonScoreDocComparator);
            }

            void PythonScoreDocComparator::decRef(void)
            {
                finalizeObject(pythonScoreDocComparator);
            }

            jint PythonScoreDocComparator::compare(::org::apache::lucene::search::ScoreDoc *i, ::org::apache::lucene::search::ScoreDoc *j)
            {
                PythonGIL gil;
                PyObject *pyi = wrap_ScoreDoc(i);
                PyObject *pyj = wrap_ScoreDoc(j);
                PyObject *pyn = callPython(*(PyObject **) &pythonScoreDocComparator, "compare", pyi, pyj, NULL);

                Py_DECREF(pyi);
                Py_DECREF(pyj);

                if (!pyn)
                    throw new org::osafoundation::util::PythonException();

                long n = PyInt_AsLong(pyn);
                Py_DECREF(pyn);

                return (jint) n;
            }

            jint PythonScoreDocComparator::sortType()
            {
                PythonGIL gil;
                PyObject *pyn = callPython(*(PyObject **) &pythonScoreDocComparator, "sortType", NULL);

                if (!pyn)
                    throw new org::osafoundation::util::PythonException();

                long n = PyInt_AsLong(pyn);
                Py_DECREF(pyn);

                return (jint) n;
            }

            ::java::lang::Comparable *PythonScoreDocComparator::sortValue(::org::apache::lucene::search::ScoreDoc *i)
            {
                PythonGIL gil;
                PyObject *pyi = wrap_ScoreDoc(i);
                PyObject *pyc = callPython(*(PyObject **) &pythonScoreDocComparator, "sortValue", pyi, NULL);

                Py_DECREF(pyi);

                if (!pyc)
                    throw new org::osafoundation::util::PythonException();

                ::java::lang::Comparable *jc = pc2jc(pyc);
                Py_DECREF(pyc);

                return jc;
            }

            // org.osafoundation.search.PythonFilter

            void PythonFilter::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonFilter);
            }

            void PythonFilter::decRef(void)
            {
                finalizeObject(pythonFilter);
            }

            ::java::util::BitSet *PythonFilter::bits(::org::apache::lucene::index::IndexReader *reader)
            {
                PythonGIL gil;
                PyObject *pyReader = wrap_IndexReader(reader);
                PyObject *pyb = callPython(*(PyObject **) &pythonFilter, "bits", pyReader, NULL);

                Py_DECREF(pyReader);

                if (!pyb)
                    throw new org::osafoundation::util::PythonException();

                java::util::BitSet *bits;

                if (!parseArg(pyb, "J", &java::util::BitSet::class$, &bits))
                {
                    Py_DECREF(pyb);
                    return bits;
                }
                else
                {
                    PyErr_SetObject(PyExc_TypeError, pyb);
                    Py_DECREF(pyb);
                }

                throw new org::osafoundation::util::PythonException();
            }

            // org.osafoundation.search.PythonHitCollector

            void PythonHitCollector::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonHitCollector);
            }

            void PythonHitCollector::decRef(void)
            {
                finalizeObject(pythonHitCollector);
            }

            void PythonHitCollector::collect(jint doc, jfloat score)
            {
                PythonGIL gil;
                PyObject *pyDoc = PyInt_FromLong((int) doc);
                PyObject *pyScore = PyFloat_FromDouble((double) score);
                PyObject *result = callPython(*(PyObject **) &pythonHitCollector, "collect", pyDoc, pyScore, NULL);

                Py_DECREF(pyDoc);
                Py_DECREF(pyScore);

                if (!result)
                    throw new org::osafoundation::util::PythonException();

                Py_DECREF(result);
            }

            // org.osafoundation.search.PythonSimilarity

            void PythonSimilarity::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonSimilarity);
            }

            void PythonSimilarity::decRef(void)
            {
                finalizeObject(pythonSimilarity);
            }

            jfloat PythonSimilarity::coord(jint overlap, jint maxOverlap)
            {
                PythonGIL gil;
                PyObject *pyOverlap = PyInt_FromLong((int) overlap);
                PyObject *pyMaxOverlap = PyInt_FromLong((int) maxOverlap);
                PyObject *pyf = callPython(*(PyObject **) &pythonSimilarity, "coord", pyOverlap, pyMaxOverlap, NULL);

                Py_DECREF(pyOverlap);
                Py_DECREF(pyMaxOverlap);

                if (!pyf)
                    throw new org::osafoundation::util::PythonException();

                double d = PyFloat_AsDouble(pyf);
                Py_DECREF(pyf);

                return (jfloat) d;
            }

            jfloat PythonSimilarity::idf(jint docFreq, jint numDocs)
            {
                PythonGIL gil;
                PyObject *pyDocFreq = PyInt_FromLong((int) docFreq);
                PyObject *pyNumDocs = PyInt_FromLong((int) numDocs);
                PyObject *pyf = callPython(*(PyObject **) &pythonSimilarity, "idf", pyDocFreq, pyNumDocs, NULL);

                Py_DECREF(pyDocFreq);
                Py_DECREF(pyNumDocs);

                if (!pyf)
                    throw new org::osafoundation::util::PythonException();

                double d = PyFloat_AsDouble(pyf);
                Py_DECREF(pyf);

                return (jfloat) d;
            }

            jfloat PythonSimilarity::idf(::java::util::Collection *terms,
                                         ::org::apache::lucene::search::Searcher *searcher)
            {
                PythonGIL gil;
                PyObject *pySimilarity = *(PyObject **) &pythonSimilarity;

                if (PyObject_HasAttrString(pySimilarity, "idfTerms"))
                {
                    PyObject *pyTerms =
                        jc2pl(terms, (PyObject *(*)(jobject)) wrap_Term);
                    PyObject *pySearcher = wrap_Searcher(searcher);
                    PyObject *pyf = callPython(pySimilarity, "idfTerms", pyTerms, pySearcher, NULL);

                    Py_DECREF(pyTerms);
                    Py_DECREF(pySearcher);

                    if (!pyf)
                        throw new org::osafoundation::util::PythonException();

                    double d = PyFloat_AsDouble(pyf);
                    Py_DECREF(pyf);

                    return (jfloat) d;
                }
                
                return Similarity::idf(terms, searcher);
            }

            jfloat PythonSimilarity::idf(::org::apache::lucene::index::Term *term,
                                         ::org::apache::lucene::search::Searcher *searcher)
            {
                PythonGIL gil;
                PyObject *pySimilarity = *(PyObject **) &pythonSimilarity;

                if (PyObject_HasAttrString(pySimilarity, "idfTerm"))
                {
                    PyObject *pyTerm = wrap_Term(term);
                    PyObject *pySearcher = wrap_Searcher(searcher);
                    PyObject *pyf = callPython(pySimilarity, "idfTerm", pyTerm, pySearcher, NULL);

                    Py_DECREF(pyTerm);
                    Py_DECREF(pySearcher);

                    if (!pyf)
                        throw new org::osafoundation::util::PythonException();

                    double d = PyFloat_AsDouble(pyf);
                    Py_DECREF(pyf);

                    return (jfloat) d;
                }
                
                return Similarity::idf(term, searcher);
            }

            jfloat PythonSimilarity::lengthNorm(jstring fieldName,
                                                jint numTokens)
            {
                PythonGIL gil;
                PyObject *pyFieldName = j2p(fieldName);
                PyObject *pyNumTokens = PyInt_FromLong((int) numTokens);
                PyObject *pyf = callPython(*(PyObject **) &pythonSimilarity, "lengthNorm", pyFieldName, pyNumTokens, NULL);

                Py_DECREF(pyFieldName);
                Py_DECREF(pyNumTokens);

                if (!pyf)
                    throw new org::osafoundation::util::PythonException();

                double d = PyFloat_AsDouble(pyf);
                Py_DECREF(pyf);

                return (jfloat) d;
            }

            jfloat PythonSimilarity::queryNorm(jfloat sumOfSquaredWeights)
            {
                PythonGIL gil;
                double sum = (double) sumOfSquaredWeights;
                PyObject *pySum = PyFloat_FromDouble(sum);
                PyObject *pyf = callPython(*(PyObject **) &pythonSimilarity, "queryNorm", pySum, NULL);

                Py_DECREF(pySum);

                if (!pyf)
                    throw new org::osafoundation::util::PythonException();

                double d = PyFloat_AsDouble(pyf);
                Py_DECREF(pyf);

                return (jfloat) d;
            }

            jfloat PythonSimilarity::sloppyFreq(jint distance)
            {
                PythonGIL gil;
                PyObject *pyDistance = PyInt_FromLong((int) distance);
                PyObject *pyf = callPython(*(PyObject **) &pythonSimilarity, "sloppyFreq", pyDistance, NULL);

                Py_DECREF(pyDistance);

                if (!pyf)
                    throw new org::osafoundation::util::PythonException();

                double d = PyFloat_AsDouble(pyf);
                Py_DECREF(pyf);

                return (jfloat) d;
            }

            jfloat PythonSimilarity::tf(jfloat freq)
            {
                PythonGIL gil;
                PyObject *pyFreq = PyFloat_FromDouble((double) freq);
                PyObject *pyf = callPython(*(PyObject **) &pythonSimilarity, "tf", pyFreq, NULL);

                Py_DECREF(pyFreq);

                if (!pyf)
                    throw new org::osafoundation::util::PythonException();

                double d = PyFloat_AsDouble(pyf);
                Py_DECREF(pyf);

                return (jfloat) d;
            }


            // org.osafoundation.search.PythonFilteredTermEnum

            void PythonFilteredTermEnum::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonFilteredTermEnum);
            }

            void PythonFilteredTermEnum::decRef(void)
            {
                finalizeObject(pythonFilteredTermEnum);
            }

            jfloat PythonFilteredTermEnum::difference()
            {
                PythonGIL gil;
                PyObject *pyf = callPython(*(PyObject **) &pythonFilteredTermEnum, "difference", NULL);


                if (!pyf)
                    throw new org::osafoundation::util::PythonException();

                double d = PyFloat_AsDouble(pyf);
                Py_DECREF(pyf);

                return (jfloat) d;
            }

            jboolean PythonFilteredTermEnum::endEnum()
            {
                PythonGIL gil;
                PyObject *pyb = callPython(*(PyObject **) &pythonFilteredTermEnum, "endEnum", NULL);

                if (!pyb)
                    throw new org::osafoundation::util::PythonException();

                jboolean b = (jboolean) PyObject_IsTrue(pyb);
                Py_DECREF(pyb);

                return b;
            }

            void PythonFilteredTermEnum::setEnum(::org::apache::lucene::index::TermEnum *termEnum)
            {
                PythonGIL gil;
                PyObject *pyEnum = wrap_TermEnum(termEnum);
                PyObject *result = callPython(*(PyObject **) &pythonFilteredTermEnum, "setEnum", pyEnum, NULL);

                Py_DECREF(pyEnum);

                if (!result)
                    throw new org::osafoundation::util::PythonException();

                Py_DECREF(result);
            }

            jboolean PythonFilteredTermEnum::termCompare(::org::apache::lucene::index::Term *term)
            {
                PythonGIL gil;
                PyObject *pyTerm = wrap_Term(term);
                PyObject *pyb = callPython(*(PyObject **) &pythonFilteredTermEnum, "termCompare", pyTerm, NULL);

                Py_DECREF(pyTerm);

                if (!pyb)
                    throw new org::osafoundation::util::PythonException();

                jboolean b = (jboolean) PyObject_IsTrue(pyb);
                Py_DECREF(pyb);

                return b;
            }


            // org.osafoundation.search.PythonSearcher

            void PythonSearcher::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonSearcher);
            }

            void PythonSearcher::decRef(void)
            {
                finalizeObject(pythonSearcher);
            }

            void PythonSearcher::close()
            {
                PythonGIL gil;
                PyObject *result = callPython(*(PyObject **) &pythonSearcher, "close", NULL);

                if (!result)
                    throw new org::osafoundation::util::PythonException();

                Py_DECREF(result);
            }

            jint PythonSearcher::docFreq(::org::apache::lucene::index::Term *term)
            {
                PythonGIL gil;
                PyObject *pyTerm = wrap_Term(term);
                PyObject *pyn = callPython(*(PyObject **) &pythonSearcher, "docFreq", pyTerm, NULL);

                Py_DECREF(pyTerm);

                if (!pyn)
                    throw new org::osafoundation::util::PythonException();

                long n = PyInt_AsLong(pyn);
                Py_DECREF(pyn);

                return (jint) n;
            }

            ::org::apache::lucene::document::Document *PythonSearcher::doc(jint i)
            {
                PythonGIL gil;
                PyObject *pyi = PyInt_FromLong((int) i);
                PyObject *pyDocument = callPython(*(PyObject **) &pythonSearcher, "doc", pyi, NULL);

                Py_DECREF(pyi);

                if (!pyDocument)
                    throw new org::osafoundation::util::PythonException();

                org::apache::lucene::document::Document *doc;
                
                if (!parseArg(pyDocument, "J",
                              &org::apache::lucene::document::Document::class$,
                              &doc))
                {
                    Py_DECREF(pyDocument);
                    return doc;
                }
                else
                {
                    PyErr_SetObject(PyExc_TypeError, pyDocument);
                    Py_DECREF(pyDocument);
                }

                throw new org::osafoundation::util::PythonException();
            }

            jint PythonSearcher::maxDoc()
            {
                PythonGIL gil;
                PyObject *pyn = callPython(*(PyObject **) &pythonSearcher, "maxDoc", NULL);

                if (!pyn)
                    throw new org::osafoundation::util::PythonException();

                long n = PyInt_AsLong(pyn);
                Py_DECREF(pyn);

                return (jint) n;
            }

            void PythonSearcher::search(::org::apache::lucene::search::Weight *weight, ::org::apache::lucene::search::Filter *filter, ::org::apache::lucene::search::HitCollector *hitCollector)
            {
                PythonGIL gil;
                PyObject *pyWeight = wrap_Weight(weight);
                PyObject *pyFilter = wrap_Filter(filter);
                PyObject *pyHitCollector = wrap_HitCollector(hitCollector);
                PyObject *result = callPython(*(PyObject **) &pythonSearcher, "searchAll", pyWeight, pyFilter, pyHitCollector, NULL);

                Py_DECREF(pyWeight);
                Py_DECREF(pyFilter);
                Py_DECREF(pyHitCollector);

                if (!result)
                    throw new org::osafoundation::util::PythonException();

                Py_DECREF(result);
            }

            ::org::apache::lucene::search::TopDocs *PythonSearcher::search(::org::apache::lucene::search::Weight *weight, ::org::apache::lucene::search::Filter *filter, jint n)
            {
                PythonGIL gil;
                PyObject *pyWeight = wrap_Weight(weight);
                PyObject *pyFilter = wrap_Filter(filter);
                PyObject *pyn = PyInt_FromLong((int) n);
                PyObject *pyTopDocs = callPython(*(PyObject **) &pythonSearcher, "search", pyWeight, pyFilter, pyn, NULL);

                Py_DECREF(pyWeight);
                Py_DECREF(pyFilter);
                Py_DECREF(pyn);

                if (!pyTopDocs)
                    throw new org::osafoundation::util::PythonException();

                org::apache::lucene::search::TopDocs *td;

                if (!parseArg(pyTopDocs, "J",
                              &org::apache::lucene::search::TopDocs::class$,
                              &td))
                {
                    Py_DECREF(pyTopDocs);
                    return td;
                }
                else
                {
                    PyErr_SetObject(PyExc_TypeError, pyTopDocs);
                    Py_DECREF(pyTopDocs);
                }                    

                throw new org::osafoundation::util::PythonException();
            }

            ::org::apache::lucene::search::TopFieldDocs *PythonSearcher::search(::org::apache::lucene::search::Weight *weight, ::org::apache::lucene::search::Filter *filter, jint n, ::org::apache::lucene::search::Sort *sort)
            {
                PythonGIL gil;
                PyObject *pyWeight = wrap_Weight(weight);
                PyObject *pyFilter = wrap_Filter(filter);
                PyObject *pyn = PyInt_FromLong((int) n);
                PyObject *pySort = wrap_Sort(sort);
                PyObject *pyTopFieldDocs = callPython(*(PyObject **) &pythonSearcher, "searchSorted", pyWeight, pyFilter, pyn, pySort, NULL);

                Py_DECREF(pyWeight);
                Py_DECREF(pyFilter);
                Py_DECREF(pyn);
                Py_DECREF(pySort);

                if (!pyTopFieldDocs)
                    throw new org::osafoundation::util::PythonException();

                org::apache::lucene::search::TopFieldDocs *tfd;

                if (!parseArg(pyTopFieldDocs, "J",
                              &org::apache::lucene::search::TopFieldDocs::class$,
                              &tfd))
                {
                    Py_DECREF(pyTopFieldDocs);
                    return tfd;
                }
                else
                {
                    PyErr_SetObject(PyExc_TypeError, pyTopFieldDocs);
                    Py_DECREF(pyTopFieldDocs);
                }                    

                throw new org::osafoundation::util::PythonException();
            }

            ::org::apache::lucene::search::Query *PythonSearcher::rewrite(::org::apache::lucene::search::Query *query)
            {
                PythonGIL gil;
                PyObject *pyQuery = wrap_Query(query);
                PyObject *pyQ = callPython(*(PyObject **) &pythonSearcher, "rewrite", pyQuery, NULL);

                Py_DECREF(pyQuery);

                if (!pyQ)
                    throw new org::osafoundation::util::PythonException();

                if (!parseArg(pyQ, "J",
                              &org::apache::lucene::search::Query::class$,
                              &query))
                {
                    Py_DECREF(pyQ);
                    return query;
                }
                else
                {
                    PyErr_SetObject(PyExc_TypeError, pyQ);
                    Py_DECREF(pyQ);
                }                    

                throw new org::osafoundation::util::PythonException();
            }

            ::org::apache::lucene::search::Explanation *PythonSearcher::explain(::org::apache::lucene::search::Weight *weight, jint doc)
            {
                PythonGIL gil;
                PyObject *pyWeight = wrap_Weight(weight);
                PyObject *pyDoc = PyInt_FromLong((int) doc);
                PyObject *pyExplanation = callPython(*(PyObject **) &pythonSearcher, "explain", pyWeight, pyDoc, NULL);

                Py_DECREF(pyWeight);
                Py_DECREF(pyDoc);

                if (!pyExplanation)
                    throw new org::osafoundation::util::PythonException();

                org::apache::lucene::search::Explanation *explanation;

                if (!parseArg(pyExplanation, "J",
                              &org::apache::lucene::search::Explanation::class$,
                              &explanation))
                {
                    Py_DECREF(pyExplanation);
                    return explanation;
                }
                else
                {
                    PyErr_SetObject(PyExc_TypeError, pyExplanation);
                    Py_DECREF(pyExplanation);
                }                    

                throw new org::osafoundation::util::PythonException();
            }
        }
    }
}
