//-----------------------------------------------------------------------------
// Copyright 2018, Oracle and/or its affiliates. All rights reserved.
//
// Licensed under BSD license (see LICENSE.txt).
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// cxoModule.h
//   Include file for all cx_Oracle source files.
//-----------------------------------------------------------------------------

#include <Python.h>
#include <structmember.h>
#include <time.h>
#include <dpi.h>

// define integer macros/methods for Python 3.x
#ifndef PyInt_Check
#define PyInt_Check                     PyLong_Check
#define PyInt_FromLong                  PyLong_FromLong
#define PyInt_AsLong                    PyLong_AsLong
#endif

// use the bytes methods in cx_Oracle and define them as the equivalent string
// type methods as is done in Python 2.6
#ifndef PyBytes_Check
    #define PyBytes_Type                PyString_Type
    #define PyBytes_AS_STRING           PyString_AS_STRING
    #define PyBytes_GET_SIZE            PyString_GET_SIZE
    #define PyBytes_Check               PyString_Check
    #define PyBytes_FromStringAndSize   PyString_FromStringAndSize
#endif

// define string/binary types and methods
#if PY_MAJOR_VERSION >= 3
    #define CXO_BASE_EXCEPTION          NULL
    #define cxoPyTypeBinary             PyBytes_Type
    #define cxoPyTypeString             PyUnicode_Type
    #define cxoPyString_fromAscii(str) \
        PyUnicode_DecodeASCII(str, strlen(str), NULL)
    #define cxoPyString_fromEncodedString(buffer, numBytes, encoding, errors) \
        PyUnicode_Decode(buffer, numBytes, encoding, errors)
#else
    #define CXO_BASE_EXCEPTION          PyExc_StandardError
    #define cxoPyTypeBinary             PyBuffer_Type
    #define cxoPyTypeString             PyString_Type
    #define cxoPyString_fromAscii(str) \
        PyBytes_FromString(str)
    #define cxoPyString_fromEncodedString(buffer, numBytes, encoding, errors) \
        PyBytes_FromStringAndSize(buffer, numBytes)
#endif

// define macros to get the build version as a string and the driver name
#define xstr(s)                         str(s)
#define str(s)                          #s
#define CXO_BUILD_VERSION_STRING        xstr(CXO_BUILD_VERSION)
#define CXO_DRIVER_NAME                 "cx_Oracle : "CXO_BUILD_VERSION_STRING

// define macro for clearing buffers
#define cxoBuffer_clear(buf)            Py_CLEAR((buf)->obj)


//-----------------------------------------------------------------------------
// Forward Declarations
//-----------------------------------------------------------------------------
typedef struct cxoBuffer cxoBuffer;
typedef struct cxoError cxoError;
typedef struct cxoConnection cxoConnection;
typedef struct cxoCursor cxoCursor;
typedef struct cxoDeqOptions cxoDeqOptions;
typedef struct cxoEnqOptions cxoEnqOptions;
typedef struct cxoFuture cxoFuture;
typedef struct cxoLob cxoLob;
typedef struct cxoMessage cxoMessage;
typedef struct cxoMessageQuery cxoMessageQuery;
typedef struct cxoMessageRow cxoMessageRow;
typedef struct cxoMessageTable cxoMessageTable;
typedef struct cxoMsgProps cxoMsgProps;
typedef struct cxoObject cxoObject;
typedef struct cxoObjectAttr cxoObjectAttr;
typedef struct cxoObjectType cxoObjectType;
typedef struct cxoSessionPool cxoSessionPool;
typedef struct cxoSubscr cxoSubscr;
typedef struct cxoVar cxoVar;
typedef struct cxoVarType cxoVarType;


//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------

// exception objects
extern PyObject *cxoWarningException;
extern PyObject *cxoErrorException;
extern PyObject *cxoInterfaceErrorException;
extern PyObject *cxoDatabaseErrorException;
extern PyObject *cxoDataErrorException;
extern PyObject *cxoOperationalErrorException;
extern PyObject *cxoIntegrityErrorException;
extern PyObject *cxoInternalErrorException;
extern PyObject *cxoProgrammingErrorException;
extern PyObject *cxoNotSupportedErrorException;

// type objects
extern PyTypeObject cxoPyTypeBfileVar;
extern PyTypeObject cxoPyTypeBinaryVar;
extern PyTypeObject cxoPyTypeBlobVar;
extern PyTypeObject cxoPyTypeBooleanVar;
extern PyTypeObject cxoPyTypeClobVar;
extern PyTypeObject cxoPyTypeConnection;
extern PyTypeObject cxoPyTypeCursor;
extern PyTypeObject cxoPyTypeCursorVar;
extern PyTypeObject cxoPyTypeDateTimeVar;
extern PyTypeObject cxoPyTypeDeqOptions;
extern PyTypeObject cxoPyTypeEnqOptions;
extern PyTypeObject cxoPyTypeError;
extern PyTypeObject cxoPyTypeFixedCharVar;
extern PyTypeObject cxoPyTypeFixedNcharVar;
extern PyTypeObject cxoPyTypeFuture;
extern PyTypeObject cxoPyTypeIntervalVar;
extern PyTypeObject cxoPyTypeLob;
extern PyTypeObject cxoPyTypeLongBinaryVar;
extern PyTypeObject cxoPyTypeLongStringVar;
extern PyTypeObject cxoPyTypeMsgProps;
extern PyTypeObject cxoPyTypeMessage;
extern PyTypeObject cxoPyTypeMessageQuery;
extern PyTypeObject cxoPyTypeMessageRow;
extern PyTypeObject cxoPyTypeMessageTable;
extern PyTypeObject cxoPyTypeNativeFloatVar;
extern PyTypeObject cxoPyTypeNativeIntVar;
extern PyTypeObject cxoPyTypeNcharVar;
extern PyTypeObject cxoPyTypeNclobVar;
extern PyTypeObject cxoPyTypeNumberVar;
extern PyTypeObject cxoPyTypeObject;
extern PyTypeObject cxoPyTypeObjectAttr;
extern PyTypeObject cxoPyTypeObjectType;
extern PyTypeObject cxoPyTypeObjectVar;
extern PyTypeObject cxoPyTypeRowidVar;
extern PyTypeObject cxoPyTypeSessionPool;
extern PyTypeObject cxoPyTypeStringVar;
extern PyTypeObject cxoPyTypeSubscr;
extern PyTypeObject cxoPyTypeTimestampVar;

// datetime types
extern PyTypeObject *cxoPyTypeDate;
extern PyTypeObject *cxoPyTypeDateTime;

// ODPI-C context and version information
extern dpiContext *cxoDpiContext;
extern dpiVersionInfo cxoClientVersionInfo;

// future object
extern cxoFuture *cxoFutureObj;


//-----------------------------------------------------------------------------
// Transforms
//-----------------------------------------------------------------------------
typedef enum {
    CXO_TRANSFORM_NONE = 0,
    CXO_TRANSFORM_BINARY,
    CXO_TRANSFORM_BFILE,
    CXO_TRANSFORM_BLOB,
    CXO_TRANSFORM_BOOLEAN,
    CXO_TRANSFORM_CLOB,
    CXO_TRANSFORM_CURSOR,
    CXO_TRANSFORM_DATE,
    CXO_TRANSFORM_DATETIME,
    CXO_TRANSFORM_DECIMAL,
    CXO_TRANSFORM_FIXED_CHAR,
    CXO_TRANSFORM_FIXED_NCHAR,
    CXO_TRANSFORM_FLOAT,
    CXO_TRANSFORM_INT,
    CXO_TRANSFORM_LONG_BINARY,
    CXO_TRANSFORM_LONG_INT,
    CXO_TRANSFORM_LONG_STRING,
    CXO_TRANSFORM_NATIVE_DOUBLE,
    CXO_TRANSFORM_NATIVE_FLOAT,
    CXO_TRANSFORM_NATIVE_INT,
    CXO_TRANSFORM_NCLOB,
    CXO_TRANSFORM_NSTRING,
    CXO_TRANSFORM_OBJECT,
    CXO_TRANSFORM_ROWID,
    CXO_TRANSFORM_STRING,
    CXO_TRANSFORM_TIMEDELTA,
    CXO_TRANSFORM_TIMESTAMP,
    CXO_TRANSFORM_TIMESTAMP_LTZ,
    CXO_TRANSFORM_UNSUPPORTED
} cxoTransformNum;


//-----------------------------------------------------------------------------
// Structures
//-----------------------------------------------------------------------------
struct cxoBuffer {
    const char *ptr;
    uint32_t numCharacters;
    uint32_t size;
    PyObject *obj;
};

struct cxoError {
    PyObject_HEAD
    long code;
    unsigned offset;
    PyObject *message;
    PyObject *context;
    char isRecoverable;
};

struct cxoConnection {
    PyObject_HEAD
    dpiConn *handle;
    cxoSessionPool *sessionPool;
    PyObject *inputTypeHandler;
    PyObject *outputTypeHandler;
    PyObject *username;
    PyObject *dsn;
    PyObject *version;
    dpiEncodingInfo encodingInfo;
    int autocommit;
};

struct cxoCursor {
    PyObject_HEAD
    dpiStmt *handle;
    dpiStmtInfo stmtInfo;
    cxoConnection *connection;
    PyObject *statement;
    PyObject *statementTag;
    PyObject *bindVariables;
    PyObject *fetchVariables;
    PyObject *rowFactory;
    PyObject *inputTypeHandler;
    PyObject *outputTypeHandler;
    uint32_t arraySize;
    uint32_t bindArraySize;
    uint32_t fetchArraySize;
    int setInputSizes;
    uint64_t rowCount;
    uint32_t fetchBufferRowIndex;
    uint32_t numRowsInFetchBuffer;
    int moreRowsToFetch;
    int isScrollable;
    int fixupRefCursor;
    int isOpen;
};

struct cxoDeqOptions {
    PyObject_HEAD
    dpiDeqOptions *handle;
    const char *encoding;
};

struct cxoEnqOptions {
    PyObject_HEAD
    dpiEnqOptions *handle;
    const char *encoding;
};

struct cxoFuture {
    PyObject_HEAD
    int contextManagerClose;
    int dmlReturningArray;
};

struct cxoLob {
    PyObject_HEAD
    cxoConnection *connection;
    dpiOracleTypeNum oracleTypeNum;
    dpiLob *handle;
};

struct cxoMessage {
    PyObject_HEAD
    cxoSubscr *subscription;
    dpiEventType type;
    PyObject *dbname;
    PyObject *txId;
    PyObject *tables;
    PyObject *queries;
    PyObject *queueName;
    PyObject *consumerName;
    int registered;
};

struct cxoMessageQuery {
    PyObject_HEAD
    uint64_t id;
    dpiOpCode operation;
    PyObject *tables;
};

struct cxoMessageRow {
    PyObject_HEAD
    PyObject *rowid;
    dpiOpCode operation;
};

struct cxoMessageTable {
    PyObject_HEAD
    PyObject *name;
    PyObject *rows;
    dpiOpCode operation;
};

struct cxoMsgProps {
    PyObject_HEAD
    dpiMsgProps *handle;
    const char *encoding;
};

struct cxoObject {
    PyObject_HEAD
    cxoObjectType *objectType;
    dpiObject *handle;
};

struct cxoObjectAttr {
    PyObject_HEAD
    PyObject *name;
    dpiObjectAttr *handle;
    dpiOracleTypeNum oracleTypeNum;
    cxoTransformNum transformNum;
    cxoObjectType *type;
};

struct cxoObjectType {
    PyObject_HEAD
    dpiObjectType *handle;
    PyObject *schema;
    PyObject *name;
    PyObject *attributes;
    PyObject *attributesByName;
    cxoConnection *connection;
    dpiOracleTypeNum elementOracleTypeNum;
    cxoTransformNum elementTransformNum;
    PyObject *elementType;
    char isCollection;
};

struct cxoSessionPool {
    PyObject_HEAD
    dpiPool *handle;
    uint32_t minSessions;
    uint32_t maxSessions;
    uint32_t sessionIncrement;
    uint32_t cacheSize;
    dpiEncodingInfo encodingInfo;
    int homogeneous;
    int externalAuth;
    PyObject *username;
    PyObject *dsn;
    PyObject *name;
    PyTypeObject *connectionType;
};

struct cxoSubscr {
    PyObject_HEAD
    dpiSubscr *handle;
    cxoConnection *connection;
    PyObject *callback;
    uint32_t namespace;
    PyObject *name;
    uint32_t protocol;
    PyObject *ipAddress;
    uint32_t port;
    uint32_t timeout;
    uint32_t operations;
    uint32_t qos;
    uint8_t groupingClass;
    uint32_t groupingValue;
    uint8_t groupingType;
    uint64_t id;
};

struct cxoVar {
    PyObject_HEAD
    dpiVar *handle;
    dpiData *data;
    cxoConnection *connection;
    PyObject *inConverter;
    PyObject *outConverter;
    cxoObjectType *objectType;
    const char *encodingErrors;
    uint32_t allocatedElements;
    uint32_t size;
    uint32_t bufferSize;
    int isArray;
    int isValueSet;
    int getReturnedData;
    cxoVarType *type;
};

struct cxoVarType {
    cxoTransformNum transformNum;
    PyTypeObject *pythonType;
    uint32_t size;
};


//-----------------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------------
int cxoBuffer_fromObject(cxoBuffer *buf, PyObject *obj, const char *encoding);
int cxoBuffer_init(cxoBuffer *buf);

int cxoConnection_isConnected(cxoConnection *conn);

int cxoCursor_performBind(cxoCursor *cursor);
int cxoCursor_setBindVariables(cxoCursor *cursor, PyObject *parameters,
        unsigned numElements, unsigned arrayPos, int deferTypeAssignment);

cxoDeqOptions *cxoDeqOptions_new(cxoConnection *connection);

cxoEnqOptions *cxoEnqOptions_new(cxoConnection *connection);

cxoError *cxoError_newFromInfo(dpiErrorInfo *errorInfo);
int cxoError_raiseAndReturnInt(void);
PyObject *cxoError_raiseAndReturnNull(void);
int cxoError_raiseFromInfo(dpiErrorInfo *errorInfo);
PyObject *cxoError_raiseFromString(PyObject *exceptionType,
        const char *message);

PyObject *cxoLob_new(cxoConnection *connection, dpiOracleTypeNum oracleTypeNum,
        dpiLob *handle);

cxoMsgProps *cxoMsgProps_new(cxoConnection*);

int cxoObject_internalExtend(cxoObject *obj, PyObject *sequence);
PyObject *cxoObject_new(cxoObjectType *objectType, dpiObject *handle);

cxoObjectAttr *cxoObjectAttr_new(cxoConnection *connection,
        dpiObjectAttr *handle);

cxoObjectType *cxoObjectType_new(cxoConnection *connection,
        dpiObjectType *handle);
cxoObjectType *cxoObjectType_newByName(cxoConnection *connection,
        PyObject *name);

void cxoSubscr_callback(cxoSubscr *subscr, dpiSubscrMessage *message);

PyObject *cxoTransform_dateFromTicks(PyObject *args);
int cxoTransform_fromPython(cxoTransformNum transformNum, PyObject *pyValue,
        dpiDataBuffer *dbValue, cxoBuffer *buffer, const char *encoding,
        const char *nencoding, cxoVar *var, uint32_t arrayPos);
cxoTransformNum cxoTransform_getNumFromDataTypeInfo(dpiDataTypeInfo *info);
cxoTransformNum cxoTransform_getNumFromType(PyTypeObject *type);
cxoTransformNum cxoTransform_getNumFromValue(PyObject *value, int plsql);
void cxoTransform_getTypeInfo(cxoTransformNum transformNum,
        dpiOracleTypeNum *oracleTypeNum, dpiNativeTypeNum *nativeTypeNum);
int cxoTransform_init(void);
PyObject *cxoTransform_timestampFromTicks(PyObject *args);
PyObject *cxoTransform_toPython(cxoTransformNum transformNum, 
        cxoConnection *connection, cxoObjectType *objType,
        dpiDataBuffer *dbValue, const char *encodingErrors);

PyObject *cxoUtils_formatString(const char *format, PyObject *args);
const char *cxoUtils_getAdjustedEncoding(const char *encoding);
int cxoUtils_getBooleanValue(PyObject *obj, int defaultValue, int *value);
int cxoUtils_getModuleAndName(PyTypeObject *type, PyObject **module,
        PyObject **name);
int cxoUtils_initializeDPI(void);

cxoVarType *cxoVarType_fromDataTypeInfo(dpiDataTypeInfo *info);
cxoVarType *cxoVarType_fromPythonType(PyTypeObject *type);
cxoVarType *cxoVarType_fromPythonValue(PyObject *value, int *isArray,
        Py_ssize_t *size, Py_ssize_t *numElements, int plsql);

int cxoVar_bind(cxoVar *var, cxoCursor *cursor, PyObject *name, uint32_t pos);
int cxoVar_check(PyObject *object);
PyObject *cxoVar_getSingleValue(cxoVar *var, dpiData *data, uint32_t arrayPos);
PyObject *cxoVar_getValue(cxoVar *var, uint32_t arrayPos);
cxoVar *cxoVar_new(cxoCursor *cursor, Py_ssize_t numElements, cxoVarType *type,
        Py_ssize_t size, int isArray, cxoObjectType *objType);
cxoVar *cxoVar_newByType(cxoCursor *cursor, PyObject *value,
        uint32_t numElements);
cxoVar *cxoVar_newByValue(cxoCursor *cursor, PyObject *value,
        Py_ssize_t numElements);
int cxoVar_setValue(cxoVar *var, uint32_t arrayPos, PyObject *value);

