//--------------------------------------------------------------------------
// Name:        wxpy_api.sip
// Purpose:     The implementation of the API functions that are exported
//              from the core extension module.
//
// Author:      Robin Dunn
//
// Created:     19-Nov-2010
// Copyright:   (c) 2010-2018 by Total Control Software
// Licence:     wxWindows license
//--------------------------------------------------------------------------


%ModuleHeaderCode
#include <wxPython/wxpy_api.h>
%End

//--------------------------------------------------------------------------


%ModuleCode
// wxPython's API helper and utility functions

//--------------------------------------------------------------------------
// wxString conversion

// See also the wxString MappedType.  This code is similar, but doesn't
// allocate a new wxString instance on the heap, is able to convert
// non-string/unicode objects to unicode, and won't raise exceptions
static wxString i_Py2wxString(PyObject* source)
{
#if wxUSE_UNICODE_WCHAR == 0
#error wxString conversion can only handle WCHAR wxStrings currently
#endif
    PyErr_Clear();
    PyObject* uni = source;
    if (PyBytes_Check(source)) {
        // if it's a string object convert it to unicode first, assumes utf-8
        uni = PyUnicode_FromEncodedObject(source, "utf-8", "strict");
        if (PyErr_Occurred()) {
            PyErr_Clear();
            return wxEmptyString;
        }
    }
    else if (!PyUnicode_Check(source)) {
#if PY_MAJOR_VERSION >= 3
        uni = PyObject_Str(source);
#else
        uni = PyObject_Unicode(source);
#endif
        if (PyErr_Occurred()) {
            PyErr_Clear();
            return wxEmptyString;
        }
    }
    wxString target;
    size_t len = PyUnicode_GET_SIZE(uni);
    if (len) {
        wxPyUnicode_AsWideChar(uni, wxStringBuffer(target, len), len);
    }
    if (!PyUnicode_Check(source))
        Py_DECREF(uni);  // release the temporary Unicode object we created
    return target;
}


// TODO:  This might be a good way to share the string conversion code here and in string.sip...
// A function to convert a Python string or unicode object to a wxString
// NOTE that it is inline so it should go in the header section
//inline wxString Py2wxString(PyObject* obj, bool setException=false, int& isErr=0) {
//    wxString str;
//    PyObject* uni = obj;
//    if (PyBytes_Check(obj)) {
//        // if it's a string object convert it to unicode first, assuming utf-8
//        uni = PyUnicode_FromEncodedObject(sipPy, "utf-8", "strict");
//        if (PyErr_Occurred()) {
//            if (setException) {
//                isErr = 1;
//            }
//            else {
//                PyErr_Clear();
//            }
//            return wxEmptyString;
//        }
//    }
//    // TODO: Coerce non-unicode types to unicode here?  (Classic does)
//    size_t len = PyUnicode_GET_SIZE(uni);
//    if (len) {
//        wxPyUnicode_AsWideChar(uni, wxStringBuffer(str, len), len);
//    }
//    if (obj != uni)
//        Py_DECREF(uni) // release the temporary Unicode object we may have created
//    return str;
//}

//--------------------------------------------------------------------------
// Wrapped object checks and converters
//
// TODO: Add some versions of these helpers that take a sipTypeDef
// instead of a name? They're accessible everywhere we need them, and
// it may be enough of an efficiency boost to make it worth it.

// Create a PyObject of the requested type from a void* and a class name.
static PyObject* i_wxPyConstructObject(void* ptr,
                                       const wxString& className,
                                       bool setThisOwn)
{
    wxString name = className;
    wxString nsDelimiter = "::";
    int pos = name.Find(nsDelimiter);
    if (pos != wxNOT_FOUND)
        name = name.Mid(pos + nsDelimiter.Len());

    const sipTypeDef* td = sipFindType(name);
    if (!td)
        return NULL;
    PyObject* transferObj = setThisOwn ? Py_None : NULL;
    return sipConvertFromType(ptr, td, transferObj);
}


// Check if a PyObject is a wrapped type
static bool i_wxPyWrappedPtr_Check(PyObject* obj)
{
    return PyObject_TypeCheck(obj, sipWrapper_Type);
}


// Check if a PyObject is a specific wrapped class or subclass
static bool i_wxPyWrappedPtr_TypeCheck(PyObject* obj, const wxString& className)
{
    const sipTypeDef* td = sipFindType(className);
    if (!td)
        return false;
    return sipCanConvertToType(obj, td, SIP_NO_CONVERTORS);
}


// Convert a wrapped SIP object to its C++ pointer, ensuring that it is of the expected type
static bool i_wxPyConvertWrappedPtr(PyObject* obj, void **ptr, const wxString& className)
{
    const sipTypeDef* td = sipFindType(className);
    if (!td)
        return false;
    if (! sipCanConvertToType(obj, td, SIP_NO_CONVERTORS))
        return false;
    int sipIsErr = 0;
    *ptr = sipConvertToType(obj, td, NULL, SIP_NO_CONVERTORS, 0, &sipIsErr);
    return true;
}


//--------------------------------------------------------------------------
// Deal with the GIL

// Calls from wxWindows back to Python code, or even any PyObject
// manipulations, PyDECREF's and etc. should be wrapped in calls to these functions:
static wxPyBlock_t i_wxPyBeginBlockThreads()
{
    if (! Py_IsInitialized()) {
        return (wxPyBlock_t)0;
    }
    PyGILState_STATE state = PyGILState_Ensure();
    return state;
}

static void i_wxPyEndBlockThreads(wxPyBlock_t blocked)
{
    if (! Py_IsInitialized()) {
        return;
    }
    PyGILState_Release(blocked);
}

//--------------------------------------------------------------------------
// Commonly used helpers for converting small sequences of numbers
// TODO: Are these still needed?

// A helper for converting a 2 element sequence to a pair of integers
static bool i_wxPy2int_seq_helper(PyObject* source, int* i1, int* i2)
{
    bool isFast = PyList_Check(source) || PyTuple_Check(source);
    PyObject *o1, *o2;

    if (!PySequence_Check(source) || PySequence_Length(source) != 2)
        return false;

    if (isFast) {
        o1 = PySequence_Fast_GET_ITEM(source, 0);
        o2 = PySequence_Fast_GET_ITEM(source, 1);
    }
    else {
        o1 = PySequence_GetItem(source, 0);
        o2 = PySequence_GetItem(source, 1);
    }

    *i1 = wxPyInt_AsLong(o1);
    *i2 = wxPyInt_AsLong(o2);

    if (! isFast) {
        Py_DECREF(o1);
        Py_DECREF(o2);
    }
    return true;
}

// A helper for converting a 4 element sequence to a set of integers
static bool i_wxPy4int_seq_helper(PyObject* source, int* i1, int* i2, int* i3, int* i4)
{
    bool isFast = PyList_Check(source) || PyTuple_Check(source);
    PyObject *o1, *o2, *o3, *o4;

    if (!PySequence_Check(source) || PySequence_Length(source) != 4)
        return false;

    if (isFast) {
        o1 = PySequence_Fast_GET_ITEM(source, 0);
        o2 = PySequence_Fast_GET_ITEM(source, 1);
        o3 = PySequence_Fast_GET_ITEM(source, 2);
        o4 = PySequence_Fast_GET_ITEM(source, 3);
    }
    else {
        o1 = PySequence_GetItem(source, 0);
        o2 = PySequence_GetItem(source, 1);
        o3 = PySequence_GetItem(source, 2);
        o4 = PySequence_GetItem(source, 3);
    }

    *i1 = wxPyInt_AsLong(o1);
    *i2 = wxPyInt_AsLong(o2);
    *i3 = wxPyInt_AsLong(o3);
    *i4 = wxPyInt_AsLong(o4);

    if (! isFast) {
        Py_DECREF(o1);
        Py_DECREF(o2);
        Py_DECREF(o3);
        Py_DECREF(o4);
    }
    return true;
}


//--------------------------------------------------------------------------
// wxVariant helpers


// A wxVariantData class that can hold a PyObject
class wxVariantDataPyObject : public wxPyUserDataHelper<wxVariantData>
{
public:
    explicit wxVariantDataPyObject(PyObject* obj = 0)
        : wxPyUserDataHelper<wxVariantData>(obj) {}

    virtual bool Eq(wxVariantData& data) const;

    virtual wxString GetType() const { return wxT("PyObject"); }
    wxVariantData* Clone() const { return new wxVariantDataPyObject(BorrowData()); }
};

bool wxVariantDataPyObject::Eq(wxVariantData& data) const
{
    wxASSERT_MSG( (data.GetType() == wxT("PyObject")),
                  wxT("wxVariantDataPyObject::Eq: argument mismatch") );
    wxVariantDataPyObject& otherData = (wxVariantDataPyObject&) data;

    wxPyThreadBlocker blocker;
    return PyObject_RichCompareBool(BorrowData(), otherData.BorrowData(), Py_EQ);
}




// Helper functions for the wxVariant mapped type. For the basic types that
// wxVariant knows about we will try to store/fetch natively, otherwise we'll
// just carry the PyObject through.
//
// These functions are here in the API so they can be used by both the
// wxVariant MappedType and by other classes or types that want to add support
// for additional kinds of natively supported types (see dataview for example.)

// PyObject --> wxVariant
wxVariant i_wxVariant_in_helper(PyObject* obj)
{
    wxVariant value;

    PyErr_Clear();

    if (PyBytes_Check(obj) || PyUnicode_Check(obj))
        value = Py2wxString(obj);

    else if (PyBool_Check(obj))
        value = (obj == Py_True);

    else if (wxPyInt_Check(obj))
        value = (long)wxPyInt_AS_LONG(obj);

    else if (PyLong_Check(obj))
        value = (long)PyLong_AsLong(obj);

    else if (PyFloat_Check(obj))
        value = PyFloat_AS_DOUBLE(obj);

    else if (obj == Py_None)
        value.MakeNull();

    else if (sipCanConvertToType(obj, sipType_wxDateTime, 0)) {
        wxDateTime* ptr;
        int state = 0;
        int isErr = 0;
        ptr = (wxDateTime*)sipConvertToType(obj, sipType_wxDateTime, NULL, 0, &state, &isErr);
        if (!isErr) {
            value = *ptr;
            sipReleaseType(ptr, sipType_wxDateTime, state);
        }
    }

    else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxBitmap"))) {
        wxBitmap* ptr;
        wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxBitmap"));
        value << *ptr;
    }

    else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxImage"))) {
        wxImage* ptr;
        wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxImage"));
        value << *ptr;
    }

    else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxIcon"))) {
        wxIcon* ptr;
        wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxIcon"));
        value << *ptr;
    }

    else if (wxPyWrappedPtr_TypeCheck(obj, wxT("wxColour"))) {
        wxColour* ptr;
        wxPyConvertWrappedPtr(obj, (void**)&ptr, wxT("wxColour"));
        value << *ptr;
    }

    else if (sipCanConvertToType(obj, sipType_wxArrayString, 0)) {
        wxArrayString* ptr;
        int state = 0;
        int isErr = 0;
        ptr = (wxArrayString*)sipConvertToType(obj, sipType_wxArrayString, NULL, 0, &state, &isErr);
        if (!isErr) {
            value = *ptr;
            sipReleaseType(ptr, sipType_wxArrayString, state);
        }
    }

    else {
        // Just use the PyObject itself
        PyErr_Clear();
        value = new wxVariantDataPyObject(obj);
    }

    return value;
}


// wxVariant --> PyObject
PyObject* i_wxVariant_out_helper(const wxVariant& value)
{
    PyObject* obj;

    if (value.IsNull()) {
        obj = Py_None;
        Py_INCREF(obj);
    }

    else if (value.IsType("string"))
        obj = wx2PyString(value.GetString());

    else if (value.IsType("bool"))
        obj = PyBool_FromLong((long)value.GetBool());

    else if (value.IsType("long"))
        obj = PyLong_FromLong(value.GetLong());

    else if (value.IsType("double"))
        obj = PyFloat_FromDouble(value.GetDouble());

    else if ( value.IsType("datetime") ) {
        wxDateTime val = value.GetDateTime();
        obj = wxPyConstructObject(new wxDateTime(val), "wxDateTime", true);
    }

    else if ( value.IsType("wxBitmap") ) {
        wxBitmap val;
        val << value;
        obj = wxPyConstructObject(new wxBitmap(val), "wxBitmap", true);
    }

    else if ( value.IsType("wxImage") ) {
        wxImage val;
        val << value;
        obj = wxPyConstructObject(new wxImage(val), "wxImage", true);
    }

    else if ( value.IsType("wxIcon") ) {
        wxIcon val;
        val << value;
        obj = wxPyConstructObject(new wxIcon(val), "wxIcon", true);
    }

    else if ( value.IsType("wxColour") ) {
        wxColour val;
        val << value;
        obj = wxPyConstructObject(new wxColour(val), "wxColour", true);
    }

    else if ( value.IsType("arrstring") ) {
        wxArrayString arr = value.GetArrayString();
        obj = sipConvertFromType(&arr, sipType_wxArrayString, NULL);
    }

    else if ( value.IsType("PyObject") ) {
        wxVariantDataPyObject* data = (wxVariantDataPyObject*)value.GetData();
        obj = data->GetData();
    }

    else {
        wxString msg = "Unexpected type (\"" + value.GetType() + "\") in wxVariant.";
        wxPyErr_SetString(PyExc_TypeError, msg.mb_str());
        obj = NULL;
    }

    return obj;
}


//--------------------------------------------------------------------------
// Check if the app object has been created. Raises an exception if not.

// Exception for when the wx.App hasn't been created yet
// (Initialized in wxPyCoreModuleInject)
PyObject* wxPyNoAppError = NULL;

bool i_wxPyCheckForApp(bool raiseException) {
    if (wxApp::GetInstance() != NULL)
        return true;
    else {
        if (raiseException)
            PyErr_SetString(wxPyNoAppError, "The wx.App object must be created first!");
        return false;
    }
}

//--------------------------------------------------------------------------
// Make a memory view object from a C buffer and size.

PyObject* i_wxPyMakeBuffer(void* ptr, Py_ssize_t len, bool readOnly=false) {
    // GIL should already be held
    if (ptr && len) {
        Py_buffer view;
        int flags = PyBUF_FORMAT|PyBUF_ND;
        if (!readOnly)
            flags |= PyBUF_WRITABLE;
        PyBuffer_FillInfo(&view, NULL, ptr, len, readOnly ? 1:0, flags);
        return PyMemoryView_FromBuffer(&view);
    } else {
        Py_INCREF(Py_None); return Py_None;
        // return PyBytes_FromString("");    TODO: None or an empty string?
    }

//    // TODO: Consider using a sip.array object instead, like this:
//    // Create a sip.array of bytes, and then convert to a memoryview which is
//    // basically the same thing but is a documented built-in Python type
//    int flags = 0;
//    if (readOnly)
//        flags |= SIP_READ_ONLY;
//    PyObject* array = sipConvertToArray(ptr, "B", len, flags);
//    return array;
}


//--------------------------------------------------------------------------
// Check if an object is suitable for conversion to various "value" types that
// can be created from a sequence of numbers, such as wx.Point, wx.Colour,
// wx.Rect, etc.

bool i_wxPyNumberSequenceCheck(PyObject* obj, int reqLength=-1) {
    // Used in the various places where a sequence of numbers can be converted
    // to a wx type, like wxPoint, wxSize, wxColour, etc. Returns true if the
    // object is a Tuple, List or numpy Array of the proper length.

    // tuples or lists are easy
    bool isFast = (PyTuple_Check(obj) || PyList_Check(obj));

    if (!isFast ) {
        // If it's not one of those, then check for an array.
        // It's probably not a good idea to do it this way, but this allows us
        // to check if the object is a numpy array without requiring that
        // numpy be imported even for those applications that are not using it.
        if (strcmp(obj->ob_type->tp_name, "numpy.ndarray") != 0)
            return false;
    }

    // Bail out here if the length isn't given
    if (reqLength == -1)
        return true;

    // Now check that the length matches the expected length
    if (PySequence_Length(obj) != reqLength)
        return false;

    // Check that each item is a number
    for (int i=0; i<reqLength; i+=1) {
        PyObject* item;
        if (isFast)
            item = PySequence_Fast_GET_ITEM(obj, i);
        else
            item = PySequence_ITEM(obj, i);
        bool isNum = PyNumber_Check(item);
        if (!isFast)
            Py_DECREF(item);
        if (!isNum)
            return false;
    }
    return true;
}


//--------------------------------------------------------------------------
// Get the pointer to the C++ object out of the sipSimpleWrapper structure.
// Yes, it's super unsafe and probably stupid, but here it is...

void* i_wxPyGetCppPtr(sipSimpleWrapper* sipPyObj) {
    return sipPyObj->data;
}

//--------------------------------------------------------------------------
// Call the PyMethod_Self API, which is not available when the Python
// limited API is activated.

inline PyObject* i_wxPyMethod_Self(PyObject* method) {
    return PyMethod_Self(method);
}



//--------------------------------------------------------------------------
// Cleanup and reinitialize the wxModules. This is needed because sometimes an
// Extension module will first be imported *after* the wx.App has been
// created, so the wxModules in that extension will not have been registered
// and initialized because they were not yet in memory.

void i_wxPyReinitializeModules() {
    if (i_wxPyCheckForApp(false)) {
        // NOTE: We are intentionally NOT calling wxModule::CleanUpModules
        // here because that could clear some things that will not be reset
        // when used again, leading to crashes. For example, in
        // wxMSWDCImpl::DoGradientFillLinear it is saving a pointer to an API
        // function in a dyn-loaded DLL. When modules are cleaned up then that
        // DLL will be unloaded, leaving a dangling function pointer.  We'll
        // likely end up with multiple instances of some things, but that is
        // better than the alternaive currently.
        //wxModule::CleanUpModules();

        wxModule::RegisterModules();
        wxModule::InitializeModules();

        // And since we're not calling CleanUpModules there is no longer any
        // need to re-init the image handlers.
        //wxInitAllImageHandlers();
    }
}

//--------------------------------------------------------------------------
// Route the various usages of the PyDate_ APIs through our API, so we only
// have to worry about PyDateTime_IMPORT being needed in one compilation unit.

int i_wxPyDateTime_Check(PyObject *obj) {
    return PyDateTime_Check(obj);
}

int i_wxPyDate_Check(PyObject *obj) {
    return PyDate_Check(obj);
}

wxDateTime* i_wxPyDateTime_ToWxDateTime(PyObject *obj) {
    return new wxDateTime(PyDateTime_GET_DAY(obj),
                          (wxDateTime::Month)(PyDateTime_GET_MONTH(obj)-1),
                          PyDateTime_GET_YEAR(obj),
                          PyDateTime_DATE_GET_HOUR(obj),
                          PyDateTime_DATE_GET_MINUTE(obj),
                          PyDateTime_DATE_GET_SECOND(obj),
                          PyDateTime_DATE_GET_MICROSECOND(obj)/1000); // micro to milli
}

wxDateTime* i_wxPyDate_ToWxDateTime(PyObject *obj) {
    return new wxDateTime(PyDateTime_GET_DAY(obj),
                          (wxDateTime::Month)(PyDateTime_GET_MONTH(obj)-1),
                          PyDateTime_GET_YEAR(obj));
}



//--------------------------------------------------------------------------
// An instance of the API structure
static wxPyAPI  API = {
    i_Py2wxString,
    i_wxPyConstructObject,
    i_wxPyBeginBlockThreads,
    i_wxPyEndBlockThreads,
    i_wxPyWrappedPtr_Check,
    i_wxPyConvertWrappedPtr,
    i_wxPy2int_seq_helper,
    i_wxPy4int_seq_helper,
    i_wxPyWrappedPtr_TypeCheck,
    i_wxVariant_in_helper,
    i_wxVariant_out_helper,
    i_wxPyCheckForApp,
    i_wxPyMakeBuffer,
    i_wxPyNumberSequenceCheck,
    i_wxPyGetCppPtr,
    i_wxPyMethod_Self,
    i_wxPyReinitializeModules,
    i_wxPyDateTime_Check,
    i_wxPyDate_Check,
    i_wxPyDateTime_ToWxDateTime,
    i_wxPyDate_ToWxDateTime
};
%End


%ModuleHeaderCode
    #include <datetime.h>
%End

%InitialisationCode
    PyDateTime_IMPORT;
%End



%PostInitialisationCode
    // Code that will run when _core is imported that will stash away a
    // pointer to the API structure.
    PyObject* wxmod = PyImport_ImportModule("wx");
    PyObject* wxmodDict = PyModule_GetDict(wxmod);
    PyObject* apiObj = PyCapsule_New(&API, "wx._wxPyAPI", NULL);
    PyDict_SetItemString(wxmodDict, "_wxPyAPI", apiObj);

    Py_XDECREF(apiObj);
    Py_DECREF(wxmod);
    wxPyGetAPIPtr();
%End

//--------------------------------------------------------------------------
