/* ====================================================================
 * 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 "org/osafoundation/store/PythonDirectory.h"
#include "org/osafoundation/store/PythonIndexInput.h"
#include "org/osafoundation/store/PythonIndexOutput.h"
#include "org/osafoundation/store/PythonLock.h"
#include "org/osafoundation/util/PythonException.h"

#include "org/apache/lucene/store/IndexOutput.h"
#include "org/apache/lucene/store/BufferedIndexOutput.h"
#include "org/apache/lucene/store/IndexInput.h"

/**
 * The native functions declared in: 
 *     org.osafoundation.store.PythonDirectory
 *     org.osafoundation.store.PythonIndexInput
 *     org.osafoundation.store.PythonIndexOutput
 *     org.osafoundation.store.PythonLock
 * 
 * @author Andi Vajda
 */

namespace org {
    namespace osafoundation {
        namespace store {

            // org.osafoundation.store.PythonDirectory

            void PythonDirectory::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonDirectory);
            }

            void PythonDirectory::decRef(void)
            {
                finalizeObject(pythonDirectory);
            }

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

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

                Py_DECREF(result);
            }

            ::org::apache::lucene::store::IndexOutput *PythonDirectory::createOutput(jstring name)
            {
                PythonGIL gil;
                PyObject *pyName = j2p(name);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "createOutput", pyName, NULL);

                Py_DECREF(pyName);

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

                jlong ptr;
                *(PyObject **) &ptr = result;
                jobject jio = new PythonIndexOutput(ptr);

                Py_DECREF(result);
                return (::org::apache::lucene::store::IndexOutput *) jio;
            }

            void PythonDirectory::deleteFile(jstring name)
            {
                PythonGIL gil;
                PyObject *pyName = j2p(name);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "deleteFile", pyName, NULL);

                Py_DECREF(pyName);

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

            jboolean PythonDirectory::fileExists(jstring name)
            {
                PythonGIL gil;
                PyObject *pyName = j2p(name);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "fileExists", pyName, NULL);

                Py_DECREF(pyName);

                if (!result)
                    throw new org::osafoundation::util::PythonException();
                
                jboolean jb = PyObject_IsTrue(result);

                Py_DECREF(result);
                return jb;
            }

            jlong PythonDirectory::fileLength(jstring name)
            {
                PythonGIL gil;
                PyObject *pyName = j2p(name);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "fileLength", pyName, NULL);

                Py_DECREF(pyName);

                if (!result)
                    throw new org::osafoundation::util::PythonException();
                
                long long n = PyLong_AsLongLong(result);

                Py_DECREF(result);
                return (jlong) n;
            }

            jlong PythonDirectory::fileModified(jstring name)
            {
                PythonGIL gil;
                PyObject *pyName = j2p(name);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "fileModified", pyName, NULL);

                Py_DECREF(pyName);

                if (!result)
                    throw new org::osafoundation::util::PythonException();
                
                long long n = PyLong_AsLongLong(result);

                Py_DECREF(result);
                return (jlong) n;
            }

            jstringArray PythonDirectory::list()
            {
                PythonGIL gil;
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "list", NULL);

                if (!result)
                    throw new org::osafoundation::util::PythonException();
                
                jstringArray array = psl2jsa(result);

                Py_DECREF(result);
                return array;
            }

            ::org::apache::lucene::store::Lock *PythonDirectory::makeLock(jstring name)
            {
                PythonGIL gil;
                PyObject *pyName = j2p(name);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "makeLock", pyName, NULL);

                Py_DECREF(pyName);

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

                jlong ptr;
                *(PyObject **) &ptr = result;
                jobject jl = new PythonLock(ptr);

                Py_DECREF(result);
                return (::org::apache::lucene::store::Lock *) jl;
            }

            ::org::apache::lucene::store::IndexInput *PythonDirectory::openInput(jstring name)
            {
                PythonGIL gil;
                PyObject *pyName = j2p(name);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "openInput", pyName, NULL);

                Py_DECREF(pyName);

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

                jlong ptr;
                *(PyObject **) &ptr = result;
                jobject jii = new PythonIndexInput(ptr);

                Py_DECREF(result);
                return (::org::apache::lucene::store::IndexInput *) jii;
            }

            void PythonDirectory::renameFile(jstring from, jstring to)
            {
                PythonGIL gil;
                PyObject *pyFrom = j2p(from);
                PyObject *pyTo = j2p(to);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "renameFile", pyFrom, pyTo, NULL);

                Py_DECREF(pyFrom);
                Py_DECREF(pyTo);

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

            void PythonDirectory::touchFile(jstring name)
            {
                PythonGIL gil;
                PyObject *pyName = j2p(name);
                PyObject *result = callPython(*(PyObject **) &pythonDirectory, "touchFile", pyName, NULL);

                Py_DECREF(pyName);

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


            // org.osafoundation.store.PythonIndexInput

            void PythonIndexInput::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonIndexInput);
            }

            void PythonIndexInput::decRef(void)
            {
                finalizeObject(pythonIndexInput);
            }

            void PythonIndexInput::close()
            {
                PythonGIL gil;
                PyObject *result = callPython(*(PyObject **) &pythonIndexInput, "close", isClone ? Py_True : Py_False, NULL);

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

                Py_DECREF(result);
            }

            jlong PythonIndexInput::length()
            {
                PythonGIL gil;
                PyObject *result = callPython(*(PyObject **) &pythonIndexInput, "length", NULL);

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

                jlong length = (jlong) PyLong_AsLongLong(result);
                Py_DECREF(result);
                if (PyErr_Occurred())
                    throw new org::osafoundation::util::PythonException();

                return length;
            }

            void PythonIndexInput::readInternal(jbyteArray b, jint offset, jint length)
            {
                PythonGIL gil;
                PyObject *pyLen = PyInt_FromLong((int) length);
                PyObject *pyPos = PyLong_FromLongLong(getFilePointer());
                PyObject *result = callPython(*(PyObject **) &pythonIndexInput, "read", pyLen, pyPos, NULL);

                Py_DECREF(pyLen);
                Py_DECREF(pyPos);

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

                jbyte *bytes = elements(b) + offset;
                int blen = JvGetArrayLength(b) - offset;
                char *ps;
                int plen;

                PyString_AsStringAndSize(result, &ps, &plen);
                
                if (length > plen)
                    length = plen;
                if (length > blen)
                    length = blen;

                while (length--)
                    *bytes++ = *ps++;

                Py_DECREF(result);
            }

            void PythonIndexInput::seekInternal(jlong pos)
            {
                PythonGIL gil;
                PyObject *pyPos = PyLong_FromLongLong(pos);
                PyObject *result = callPython(*(PyObject **) &pythonIndexInput, "seek", pyPos, NULL);

                Py_DECREF(pyPos);

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

                Py_DECREF(result);
            }


            // org.osafoundation.store.PythonIndexOutput

            void PythonIndexOutput::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonIndexOutput);
            }

            void PythonIndexOutput::decRef(void)
            {
                finalizeObject(pythonIndexOutput);
            }

            void PythonIndexOutput::close()
            {
                BufferedIndexOutput::close();
                
                PythonGIL gil;
                PyObject *result = callPython(*(PyObject **) &pythonIndexOutput, "close", NULL);

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

                Py_DECREF(result);
            }

            void PythonIndexOutput::seek(jlong pos)
            {
                BufferedIndexOutput::seek(pos);
                
                PythonGIL gil;
                PyObject *pyPos = PyLong_FromLongLong((long long) pos);
                PyObject *result = callPython(*(PyObject **) &pythonIndexOutput, "seek", pyPos, NULL);

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

                Py_DECREF(result);
            }

            jlong PythonIndexOutput::length()
            {
                PythonGIL gil;
                PyObject *result = callPython(*(PyObject **) &pythonIndexOutput, "length", NULL);

                if (!result)
                    throw new org::osafoundation::util::PythonException();
                
                long long n = PyLong_AsLongLong(result);

                Py_DECREF(result);
                return (jlong) n;
            }

            void PythonIndexOutput::flushBuffer(jbyteArray b, jint length)
            {
                PythonGIL gil;
                int len = JvGetArrayLength(b);
                jbyte *bytes = elements(b);

                if (length > len)
                    length = len;

                PyObject *pyBuffer = PyString_FromStringAndSize((char *) bytes, length);
                PyObject *result = callPython(*(PyObject **) &pythonIndexOutput, "write", pyBuffer, NULL);

                Py_DECREF(pyBuffer);

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

                Py_DECREF(result);
            }


            // org.osafoundation.store.PythonLock

            void PythonLock::incRef(void)
            {
                PythonGIL gil;
                Py_INCREF(*(PyObject **) &pythonLock);
            }

            void PythonLock::decRef(void)
            {
                finalizeObject(pythonLock);
            }

            jboolean PythonLock::isLocked()
            {
                PythonGIL gil;
                PyObject *result = callPython(*(PyObject **) &pythonLock, "isLocked", NULL);

                if (!result)
                    throw new org::osafoundation::util::PythonException();
                
                jboolean jb = PyObject_IsTrue(result);

                Py_DECREF(result);
                return jb;
            }

            jboolean PythonLock::obtain()
            {
                PythonGIL gil;
                PyObject *result = callPython(*(PyObject **) &pythonLock, "obtain", NULL);

                if (!result)
                    throw new org::osafoundation::util::PythonException();
                
                jboolean jb = PyObject_IsTrue(result);

                Py_DECREF(result);
                return jb;
            }

            jboolean PythonLock::obtain(jlong lockWaitTimeout)
            {
                PythonGIL gil;
                PyObject *pyTO = PyLong_FromLongLong(lockWaitTimeout);
                PyObject *result = callPython(*(PyObject **) &pythonLock, "obtainTimeout", pyTO, NULL);

                Py_DECREF(pyTO);

                if (!result)
                    throw new org::osafoundation::util::PythonException();
                
                jboolean jb = PyObject_IsTrue(result);

                Py_DECREF(result);
                return jb;
            }

            void PythonLock::release()
            {
                PythonGIL gil;

                if (pythonLock)
                {
                    PyObject *result = callPython(*(PyObject **) &pythonLock, "release", NULL);

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