/******************************************************************************
 *
 * Project:  GDAL Core
 * Purpose:  Python interface
 * Author:   Even Rouault, <even dot rouault at spatialys dot com>
 *
 ******************************************************************************
 * Copyright (c) 2017-2019, Even Rouault, <even dot rouault at spatialys dot com>
 *
 * 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.
 ****************************************************************************/

#ifndef GDALPYTHON_H_INCLUDED
#define GDALPYTHON_H_INCLUDED

#include "cpl_string.h"
#include <cwchar>

bool GDALPythonInitialize();

void GDALPythonFinalize();

//! @cond Doxygen_Suppress

// Subset of Python API defined as function pointers
// Only use the below function pointers if GDALPythonInitialize() succeeds
namespace GDALPy
{
    typedef struct _object PyObject;
    typedef size_t Py_ssize_t;

    extern int (*Py_IsInitialized)(void);
    extern void (*Py_SetProgramName)(const wchar_t*);
    extern void (*Py_SetPythonHome)(const wchar_t*);
    extern PyObject* (*PyObject_Type)(PyObject*);
    extern int (*PyObject_IsInstance)(PyObject*, PyObject*);
    extern PyObject* (*PyTuple_New)(size_t);
    extern PyObject* (*PyBool_FromLong)(long);
    extern PyObject* (*PyLong_FromLong)(long);
    extern long (*PyLong_AsLong)(PyObject *);
    extern PyObject* (*PyLong_FromLongLong)(GIntBig);
    extern GIntBig (*PyLong_AsLongLong)(PyObject *);
    extern PyObject* (*PyFloat_FromDouble)(double);
    extern double (*PyFloat_AsDouble)(PyObject*);
    extern PyObject* (*PyObject_Call)(PyObject*, PyObject*, PyObject*);
    extern PyObject* (*PyObject_GetIter)(PyObject*);
    extern PyObject* (*PyIter_Next)(PyObject*);
    extern void (*Py_IncRef)(PyObject*);
    extern void (*Py_DecRef)(PyObject*);
    extern PyObject* (*PyErr_Occurred)(void);
    extern void (*PyErr_Print)(void);

    extern PyObject* (*Py_CompileString)(const char*, const char*, int);
    extern PyObject* (*PyImport_ExecCodeModule)(const char*, PyObject*);
    extern int (*PyObject_HasAttrString)(PyObject*, const char*);
    extern PyObject* (*PyObject_GetAttrString)(PyObject*, const char*);
    extern int (*PyObject_SetAttrString)(PyObject*, const char*, PyObject*);
    extern int (*PyTuple_SetItem)(PyObject *, size_t, PyObject *);
    extern void (*PyObject_Print)(PyObject*,FILE*,int);

    extern Py_ssize_t (*PyBytes_Size)(PyObject *);
    extern const char* (*PyBytes_AsString)(PyObject*);
    extern PyObject* (*PyBytes_FromStringAndSize)(const void*, size_t);

    extern PyObject* (*PyUnicode_FromString)(const char*);
    extern PyObject* (*PyUnicode_AsUTF8String)(PyObject *);
    extern PyObject* (*PyImport_ImportModule)(const char*);
    extern int (*PyCallable_Check)(PyObject*);
    extern PyObject* (*PyDict_New)(void);
    extern int (*PyDict_SetItemString)(PyObject *p, const char *key,
                                    PyObject *val);
    extern int (*PyDict_Next)(PyObject *p, size_t *, PyObject **, PyObject **);
    extern PyObject* (*PyDict_GetItemString)(PyObject *p, const char *key);
    extern PyObject* (*PyList_New)(Py_ssize_t);
    extern int (*PyList_SetItem)(PyObject *, Py_ssize_t , PyObject *);
    extern int (*PyArg_ParseTuple)(PyObject *, const char *, ...);

    extern int (*PySequence_Check)(PyObject *o);
    extern Py_ssize_t (*PySequence_Size)(PyObject *o);
    extern PyObject* (*PySequence_GetItem)(PyObject *o, Py_ssize_t i);

    extern void (*PyErr_Fetch)(PyObject **poPyType, PyObject **poPyValue,
                            PyObject **poPyTraceback);
    extern void (*PyErr_Clear)(void);
    extern const char* (*Py_GetVersion)(void);

    typedef struct
    {
        //cppcheck-suppress unusedStructMember
        char big_enough[256];
    } Py_buffer;
    extern int (*PyBuffer_FillInfo)(Py_buffer *view, PyObject *obj, void *buf,
                                    size_t len, int readonly, int infoflags);
    extern PyObject* (*PyMemoryView_FromBuffer)(Py_buffer *view);


    typedef PyObject* (*PyCFunction)(PyObject*, PyObject*, PyObject*);

    typedef struct PyMethodDef PyMethodDef;
    struct PyMethodDef
    {
        const char* name;
        PyCFunction function;
        int flags;
        const char* help;
    };
    extern PyObject * (*PyModule_Create2)(struct PyModuleDef*, int);

    #define PYTHON_API_VERSION 1013

    /* Flag passed to newmethodobject */
    #define METH_VARARGS  0x0001
    #define METH_KEYWORDS 0x0002

    #define _PyObject_HEAD_EXTRA

    struct _object {
        _PyObject_HEAD_EXTRA
        Py_ssize_t ob_refcnt;
        void /*struct _typeobject*/ *ob_type;
    };

    #define PyObject_HEAD PyObject ob_base;

    #define _PyObject_EXTRA_INIT

    #define PyObject_HEAD_INIT(type)        \
        { _PyObject_EXTRA_INIT              \
        1, type },


    #define PyModuleDef_HEAD_INIT { \
        PyObject_HEAD_INIT(nullptr)    \
        nullptr, /* m_init */          \
        0,    /* m_index */         \
        nullptr, /* m_copy */          \
    }

    typedef struct PyModuleDef_Base {
        PyObject_HEAD
        PyObject* (*m_init)(void);
        Py_ssize_t m_index;
        PyObject* m_copy;
    } PyModuleDef_Base;

    typedef void* traverseproc;
    typedef void* inquiry;
    typedef void* freefunc;

    typedef struct PyModuleDef{
        PyModuleDef_Base m_base;
        const char* m_name;
        const char* m_doc;
        Py_ssize_t m_size;
        const PyMethodDef *m_methods;
        struct PyModuleDef_Slot* m_slots;
        traverseproc m_traverse;
        inquiry m_clear;
        freefunc m_free;
    } PyModuleDef;

    #define Py_file_input 257

    typedef int PyGILState_STATE;
    class GIL_Holder
    {
            bool             m_bExclusiveLock;
            PyGILState_STATE m_eState = 0;

        public:

            explicit GIL_Holder(bool bExclusiveLock);
            virtual ~GIL_Holder();
    };

    CPLString GetString(PyObject* obj, bool bEmitError = true);
    CPLString GetPyExceptionString();
    bool ErrOccurredEmitCPLError();

} // namespace GDALPy

//! @endcond

#endif
